Объект, хранящийся в сеансе Rails, становится строкой?

Обычно я бы не хранил объекты в сеансе 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'

to

Rails.application.config.session_store :cache_store

оно работает!

Любые идеи?


person Zack Chandler    schedule 10.04.2014    source источник
comment
Рассматриваемая библиотека — это гем shopify_app, используемый многими разработчиками. Я согласен, что хранить объект в сеансе Rails — плохая идея по ряду причин, но это то, что делает этот гем: github.com/Shopify/shopify_app/blob/master/lib/generators/ Мне интересно, почему это не работает в Rails 4.1? Я не вижу, чтобы это происходило в их примере приложения версии 3.2: github.com/Shopify/embedded -приложение-пример   -  person Zack Chandler    schedule 10.04.2014


Ответы (3)


Вы не можете хранить объекты в сеансе Rails. Это хранилище ключей и значений, которое принимает только строки, потому что чаще всего они упаковываются и отправляются клиенту в виде зашифрованного файла cookie.

Это не свалка для вещей, которые могут вам понадобиться. Обратите внимание на то, сколько мусора вы туда впихиваете, потому что чем больше вы полагаетесь на сеанс, тем больше куки, которые клиент должен будет отправлять обратно на ваш сервер для каждого запроса.

Стоит следить за заголовками в инструменте проверки сети вашего браузера, чтобы увидеть, насколько большой объем занимают ваши запросы.

Если вам действительно нужно что-то там сохранить, используйте удобный для строк формат кодирования, такой как JSON, чтобы быть уверенным, что вы можете вернуть данные в удобном для использования формате.

Я бы очень не решился использовать cache_store, который не используется в разных экземплярах вашего приложения. Объекты Ruby существуют только в контексте одного процесса, поэтому другие запросы, которые чаще всего попадают в какой-то случайный процесс, не смогут использовать это так же легко.

Хранилище файлов cookie по умолчанию является самым надежным. Другие, которые совместно используются процессами, зависят от работы дополнительных служб (Memcached, Redis и т. д.), но большинство из них также диктуют политику только для строк.

person tadman    schedule 10.04.2014
comment
Рассматриваемая библиотека — это гем shopify_app, используемый многими разработчиками. Я согласен, что хранить объект в сеансе Rails — плохая идея по ряду причин, но это то, что делает этот гем: github.com/Shopify/shopify_app/blob/master/lib/generators/ Мне интересно, почему это не работает в Rails 4.1? Я не вижу, чтобы это происходило в их примере приложения версии 3.2: github.com/Shopify/embedded -приложение-пример - person Zack Chandler; 10.04.2014
comment
Вы можете надежно хранить вещи, если они сериализованы. В противном случае это удар и промах. Я не уверен, зачем кому-то писать код, который зависит от того, работает ли он вопреки всему. - person tadman; 10.04.2014

Исправление заключается в явном выполнении сериализации и десериализации вручную.

e.g.

# 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 и соответствующим образом применить к существующему приложению.

person choonkeat    schedule 02.05.2014

У меня была такая же проблема с моим приложением Shopify, и я потратил много времени на его отладку. Основываясь на точном, если не догматическом, объяснении Тадмана, я смог обойти это, упорядочив объект сеанса. Для изменения потребовалось всего 3 строки кода:

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

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

Я разместил это на форуме Shopify и ожидаю, что они скоро выпустят обновление. Что бы это ни стоило, я думаю, что старый способ, хотя и «неправильный» с философской точки зрения, работал правильно во всех версиях Rails до 4.1.

person Jason M    schedule 20.04.2014