【问题标题】:Object stored in Rails session becomes a String?存储在 Rails 会话中的对象变成了字符串?
【发布时间】:2014-05-23 14:36:38
【问题描述】:

通常我不会在 Rails 会话中存储对象,但我使用的库需要它。我遇到了一个非常奇怪的问题,存储的对象在重定向后显示为字符串。

为了重现我创建了一个示例 Rails 4.1 应用程序

$ rails new session-test

添加了一个测试控制器:

class HomeController < ApplicationController
  def index
    logger.debug "session[:customer]: #{session[:customer]}"
    logger.debug "session[:customer].name: #{session[:customer].name}"
  end

  def from
    Struct.new 'Customer', :name, :address
    session[:customer] = Struct::Customer.new 'Dave', '123 Main'
    redirect_to :action => :index
  end
end

设置路线:

Rails.application.routes.draw do
  get 'home/index'
  get 'home/from'
  root 'home#index'
end

然后我启动 Rails

$ bundle exec rails server

然后在浏览器中点击 localhost:3000/home/from:

Started GET "/home/from" for 127.0.0.1 at 2014-04-09 21:20:25 -0700
Processing by HomeController#from as HTML
Redirected to http://localhost:3000/home/index
Completed 302 Found in 18ms (ActiveRecord: 0.0ms)


Started GET "/home/index" for 127.0.0.1 at 2014-04-09 21:20:25 -0700
Processing by HomeController#index as HTML
session[:customer]: #<struct Struct::Customer name="Dave", address="123 Main">
Completed 500 Internal Server Error in 2ms

NoMethodError (undefined method `name' for "#<struct Struct::Customer name=\"Dave\", address=\"123 Main\">":String):
  app/controllers/home_controller.rb:4:in `index'

我不知道为什么这个对象被翻译成字符串...

这似乎与 cookie_store 的会话存储类型有关,因为如果我改变了

session_store.rb 来自

Rails.application.config.session_store :cookie_store, key: '_session-test_session'

Rails.application.config.session_store :cache_store

有效!

有什么想法吗?

【问题讨论】:

标签: ruby-on-rails session ruby-on-rails-4 shopify ruby-on-rails-4.1


【解决方案1】:

您不能在 Rails 会话中存储对象。它是一个只接受字符串的键值存储,因为它通常被打包并作为加密的 cookie 发送给客户端。

这里不是你可能需要的东西的垃圾场。请注意您在其中塞入了多少垃圾,因为您越依赖会话,cookie 就越大,客户端将不得不针对每个请求将其抛回您的服务器。

值得观察浏览器网络检查工具中的标头,以了解您的请求占用的空间有多大。

如果您确实需要在其中保留某些内容,请使用字符串友好的编码格式(如 JSON),以确保您可以以可用的格式获取数据。

我也很犹豫是否要使用cache_store,它不会在您的应用程序的不同实例之间共享。 Ruby 对象仅存在于单个进程的上下文中,因此其他请求(通常会遇到某个随机进程)将无法轻松利用它。

默认的 cookie 存储是最可靠的。在进程之间共享的其他服务取决于正在运行的其他服务(Memcached、Redis 等),但其中大多数也规定了仅字符串策略。

【讨论】:

  • 有问题的库是许多开发人员使用的 shopify_app gem。我同意在 Rails 会话中存储对象是一个坏主意,原因有很多,但这就是这个 gem 的作用:github.com/Shopify/shopify_app/blob/master/lib/generators/… 我想知道为什么这在 Rails 4.1 中很无聊?我在他们的示例应用程序 3.2 中没有看到它发生:github.com/Shopify/embedded-app-example
  • 如果它们被序列化,你可以可靠地存储它们。否则它会被击中和错过。我不确定为什么有人会编写依赖于它的代码。
【解决方案2】:

修复是手动显式执行序列化和反序列化。

例如

# storing...
session[:customer] = (Struct::Customer.new 'Dave', '123 Main').to_yaml

# retrieving...
customer = YAML.load(session[:customer])

对于shopify_app gem,请参阅拉取请求https://github.com/Shopify/shopify_app/pull/90/files 中的文件更改并相应地应用于您现有的应用程序。

【讨论】:

    【解决方案3】:

    我的 Shopify 应用也遇到了同样的问题,并且花了很多时间进行调试。基于 tadman 的准确(如果不是教条)解释,我能够通过编组会话对象来解决它。修改只需要 3 行代码:

    login_protection.rb:12: ShopifyAPI::Base.activate_session(Marshal::load session[:shopify]) login_protection.rb:25: Marshal::load session[:shopify]

    sessions_controller.rb:13: session[:shopify] = Marshal::dump(sess)

    我在 Shopify 论坛上发布了此消息,并希望他们很快会发布更新。对于它的价值,我认为旧方法虽然在哲学上是“错误的”,但在直到 4.1 的每个版本的 Rails 中都能正常工作。

    【讨论】:

      猜你喜欢
      • 2010-11-08
      • 2014-08-12
      • 1970-01-01
      • 1970-01-01
      • 2016-07-26
      • 1970-01-01
      • 2011-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多