如何将Rails cookies迁移到新的JSON序列化器上

194 阅读5分钟

如何从Marshal转移到新的Rails 7默认JSON序列化器。

我最近正在将Phrase升级到Rails 7。像这样的大升级,通常都是在做最简单的改动,这次也不例外。然而,Rails的每一个主要和次要版本都会带来一些新的默认值,这些默认值会随着时间的推移而积累起来,让你有一些债务需要支付。

今天我想谈谈从Rails 7.0开始成为默认的用于cookie的JSON序列器。

Marshal与JSON

那么,新的JSON序列化器是怎么回事?以前的cookie序列化器方便地使用Marshal将任何Ruby对象转换为字节流,并允许我们存储几乎任何东西,只要它适合4096字节(浏览器的限制)。这种灵活性也是为什么Marshaling不被认为是安全的,也是Rails 7改变为将cookies保存为JSON的原因。

这一变化的主要问题是,你目前的用户已经在他们的浏览器中保存了大量的cookie,所以如果你改变了序列化器而没有改变你的cookie,你会发现自己的应用程序已经坏了。

该计划

将cookie改为JSON的计划包括。

  • 升级到Rails 7,同时保留旧的Marshal序列器
  • 编制一个cookie及其数据类型的列表
  • 读取cookie时转换数据类型
  • 转移到:hybrid 串行器
  • 改变我们保存cookie的方式
  • :hybrid 转移到:json

升级到Rails 7

在升级到第7版时,Rails为我们提供了保障。运行rails app:update 后,我们会得到一个新的初始化器,所有新的Rails 7默认值都在一个地方。我们对以下部分感兴趣。

# config/initializers/new_framework_defaults_7_0.rb
...
# If you're upgrading and haven't set `cookies_serializer` previously, your cookie serializer
# was `:marshal`. Convert all cookies to JSON, using the `:hybrid` formatter.
#
#
# If you're confident all your cookies are JSON formatted, you can switch to the `:json` formatter.
#
#
# Continue to use `:marshal` for backward compatibility with old cookies.
#
#
# If you have configured the serializer elsewhere, you can remove this.
#
#
# See https://guides.rubyonrails.org/action_controller_overview.html#cookies for more information.
# Rails.application.config.action_dispatch.cookies_serializer = :hybrid

因为我们不知道我们的代码在获得JSON cookies时会发生什么,我们需要从旧的默认值开始。我们将你的序列化器设置为:marshal ,并继续进行升级。

Rails.application.config.action_dispatch.cookies_serializer = :marshal

这使我们能够升级Rails,并在需要时安全地恢复升级。一旦上了Rails 7,我们就可以尝试修复我们的cookies。

Cookie 列表

由于每个cookie的保存和读取方式不同,我们需要识别应用程序中的所有cookie(包括签名和会话cookie)。这可以从简单搜索cookiessession 开始,但不要忘了我们的依赖也可能使用cookie。你应该在你的浏览器的开发工具中寻找和检查cookie存储。做一个列表,并按你要保存的数据类型对cookie进行分组。你的营销和法律部门也可能对此感兴趣。

该列表可以是以下样子。


# Cookies

## Strings

- cookies[:language]

## Integers

- session[:oauth_access_token_id]

转换数据类型

升级到JSON时的主要问题是,cookie最终会有不同的数据类型。因此,我们应该准备一份我们发现的数据类型的清单,并确保我们了解值将如何变化。

如果你不确定会发生什么,在你的控制器中设置一个cookie,并使用新的默认值读回它。

# in config
Rails.application.config.action_dispatch.cookies_serializer = :json

# in a controller
cookies[:test] = { key: "value" }
puts cookies[:test].inspect

我将指出一些主要的数据类型,以及我们必须如何改变从这些cookie中读取数值的代码。

  • 字符串

    Marshal中的字符串将是JSON中的字符串。不需要改变。

  • 原子

    原子不会停留在原子上,而会被转换为字符串。修复方法是对来自cookie的值调用to_sym

  • 布尔类

    布尔值不会保持为布尔值,而会被返回为字符串。解决方法是使用ActiveModel::Type::Boolean.new.cast 来转换返回的值。

  • 整数

    整数不会保持为整数,而会作为字符串返回。解决方法是对返回值调用.to_i

  • 哈希值

    哈希值将保持为哈希值,但原子键将最终成为字符串。解决方法是将这样的cookie转换为HashWithIndifferentAccess 。 注意,哈希值也可以包含可能需要改变的值。

  • 红宝石对象

    最后一类是所有其他的Ruby对象,不会保持不变。例如,ActiveSupport::Duration 将被返回为一个以秒为单位的字符串。

最后,东西会变成JSON,所以如果你需要一个更复杂的结构,把它转换成JSON,然后你可以把它传给cookie。

继续前进

一旦我们更新了如何读取我们的cookie,我们就可以切换到:hybrid 模式,并部署我们的变化。

Rails.application.config.action_dispatch.cookies_serializer = :hybrid

主要的工作已经完成。让自己休息一下吧!

保存cookie

为了正确地完成工作,我们应该回到每个cookie,并确保我们已经用预期的数据类型来保存它。这将提高清晰度。因此,我们可以调用.to_s ,明确地传递一个字符串,而不是保留一个整数。

离开混合模式

在生产中经过合理的时间,我们现在可以从配置中删除:hybrid 选项(你可以删除整个设置)。你已经成功了!