Rails 解决 Rails 4 降到 Rails 3 遇到 Cookies 序列化方式不同导致的 500 问题

Zoker · 2015年10月29日 · 最后由 zoker 回复于 2015年10月29日 · 2570 次阅读

在本地生产环境把Rails4 切回 Rails3的时候,一直报 500 错误

NoMethodError (undefined method `sweep' for #<Hash:0x007f2888ff0fe8>):
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/flash.rb:239:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/session/abstract/id.rb:210:in `context'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/session/abstract/id.rb:205:in `call'
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/cookies.rb:341:in `call'
  vendor/bundle/ruby/2.1.0/gems/activerecord-3.2.16/lib/active_record/query_cache.rb:64:in `call'
  vendor/bundle/ruby/2.1.0/gems/activerecord-3.2.16/lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in `call'
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
  vendor/bundle/ruby/2.1.0/gems/activesupport-3.2.16/lib/active_support/callbacks.rb:405:in `_run__2392578677261619807__call__2174462519133804783__callbacks'
  vendor/bundle/ruby/2.1.0/gems/activesupport-3.2.16/lib/active_support/callbacks.rb:405:in `__run_callback'
  vendor/bundle/ruby/2.1.0/gems/activesupport-3.2.16/lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
  vendor/bundle/ruby/2.1.0/gems/activesupport-3.2.16/lib/active_support/callbacks.rb:81:in `run_callbacks'
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/sendfile.rb:102:in `call'
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
  vendor/bundle/ruby/2.1.0/gems/railties-3.2.16/lib/rails/rack/logger.rb:32:in `call_app'
  vendor/bundle/ruby/2.1.0/gems/railties-3.2.16/lib/rails/rack/logger.rb:16:in `block in call'
  vendor/bundle/ruby/2.1.0/gems/activesupport-3.2.16/lib/active_support/tagged_logging.rb:22:in `tagged'
  vendor/bundle/ruby/2.1.0/gems/railties-3.2.16/lib/rails/rack/logger.rb:16:in `call'
  vendor/bundle/ruby/2.1.0/gems/actionpack-3.2.16/lib/action_dispatch/middleware/request_id.rb:22:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/methodoverride.rb:21:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/runtime.rb:17:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/lock.rb:15:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-cache-1.2/lib/rack/cache/context.rb:136:in `forward'
  vendor/bundle/ruby/2.1.0/gems/rack-cache-1.2/lib/rack/cache/context.rb:245:in `fetch'
  vendor/bundle/ruby/2.1.0/gems/rack-cache-1.2/lib/rack/cache/context.rb:185:in `lookup'
  vendor/bundle/ruby/2.1.0/gems/rack-cache-1.2/lib/rack/cache/context.rb:66:in `call!'
  vendor/bundle/ruby/2.1.0/gems/rack-cache-1.2/lib/rack/cache/context.rb:51:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-utf8_sanitizer-1.3.0/lib/rack/utf8_sanitizer.rb:15:in `call'
  vendor/bundle/ruby/2.1.0/gems/railties-3.2.16/lib/rails/engine.rb:484:in `call'
  vendor/bundle/ruby/2.1.0/gems/railties-3.2.16/lib/rails/application.rb:231:in `call'
  vendor/bundle/ruby/2.1.0/gems/railties-3.2.16/lib/rails/railtie/configurable.rb:30:in `method_missing'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/builder.rb:134:in `call'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/urlmap.rb:64:in `block in call'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/urlmap.rb:49:in `each'
  vendor/bundle/ruby/2.1.0/gems/rack-1.4.5/lib/rack/urlmap.rb:49:in `call'
  vendor/bundle/ruby/2.1.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:580:in `process_client'
  vendor/bundle/ruby/2.1.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:674:in `worker_loop'
  vendor/bundle/ruby/2.1.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:529:in `spawn_missing_workers'
  vendor/bundle/ruby/2.1.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:140:in `start'
  vendor/bundle/ruby/2.1.0/gems/unicorn-4.9.0/bin/unicorn:126:in `<top (required)>'
  vendor/bundle/ruby/2.1.0/bin/unicorn:23:in `load'
  vendor/bundle/ruby/2.1.0/bin/unicorn:23:in `<main>'

然后搜到这么一篇帖子

才发现原来是Rails4Flash序列化到Cookies不同于Rails3,当Rails3尝试去反序列化Cookies中的Flash的时候,就会报如上的错误。 解决方法是如果响应不了sweep方法,那就把flash cookies删除让程序重建即可。

新建文件 config/initializers/rails4_to_rails3_downgradability.rb

if Rails::VERSION::MAJOR == 3

  module ActionDispatch
    class Flash
      def call(env)
        if (session = env['rack.session']) && (flash = session['flash'])

          # Beginning of change!

          if flash.respond_to?(:sweep)
            flash.sweep
          else
            session.delete("flash")
          end

          # End of change!

        end

        @app.call(env)
      ensure
        session    = env['rack.session'] || {}
        flash_hash = env[KEY]

        if flash_hash
          if !flash_hash.empty? || session.key?('flash')
            session["flash"] = flash_hash
            new_hash = flash_hash.dup
          else
            new_hash = flash_hash
          end

          env[KEY] = new_hash
        end

        if session.key?('flash') && session['flash'].empty?
          session.delete('flash')
        end
      end
    end
  end

end

总结:要理解各个版本之间的各种差异,才能轻松应对。

也可以考虑下直接修改 config/initializers/session_store.rb 把 key 直接改掉,就不需要打补丁了,哈哈

学习了。。。

#1 楼 @wuwx 线下还好,如果是线上就不能这么完了,所有用户都会被退出啊

为啥要降 rails 版本啊

#4 楼 @alucardpj 有点问题想对比下,就测试了下

需要 登录 后方可回复, 如果你还没有账号请 注册新账号