Rails使用Gzip压缩的JSON响应

30

我有一个使用Rails编写的API,每次请求都会返回JSON响应。

响应可能非常大,因此我需要使用gzip压缩JSON响应。

想知道如何在Rails控制器中实现这一点?

我已经添加了以下代码:

use Rack::Deflater

在config.ru文件中。

我还需要更改渲染JSON的那一行吗?

render :json => response.to_json()

还有,我如何检查响应是否为gzip格式?

我从终端执行了一个curl请求,只看到普通的JSON数据。

5个回答

37

我的文章"使用 Rack::Deflater 进行内容压缩"介绍了几种整合 Rack::Deflater 的方法。最简单的方法是只需在config/application.rb中更新:

module YourApp
  class Application < Rails::Application
    config.middleware.use Rack::Deflater
  end
end

如果客户端明确表示它们可以处理压缩,那么您将自动使用deflate / gzip压缩所有控制器响应。


我需要这个 Rack 中间件声明以及标头来获得 gzip 响应。谢谢! - Nate Bird
我在application.rb中添加了config.middleware.use Rack::Deflater,压缩只在Mac OS(Chrome、FF和Safary)上开始工作。在Windows上,我在FF和IE中收到没有'Content-Encoding: gzip;'的响应(仅在Chrome中工作)。P.S. "Accept-Encoding: gzip"在所有浏览器中都存在。你知道为什么吗? - bmalets
@bmalets 这个问题还存在吗?找到绕过的方法了吗? - Christian Fazzini
@ChristianFazzini,我记得这是旧版本的FF和IE浏览器的问题,我通过在nginx配置中启用gzip来解决了这个问题。 - bmalets
@bmalets 你还记得你所提到的FF和IE的版本吗?关于在nginx配置中添加gzipping,它是否类似于https://mattstauffer.co/blog/enabling-gzip-on-nginx-servers-including-laravel-forge - Christian Fazzini
@ChristianFazzini 请查看 http://stackoverflow.com/questions/30323996/http-compression-in-rails-not-working-for-json-responses。 - bmalets

15

如果要响应以gzip格式呈现,我们不需要更改render方法的调用。
如果请求带有Accept-Encoding: gzip头,则Rails将自动使用gzip压缩JSON响应。

如果您不希望用户发送带有预设标头的请求,可以在控制器中手动向请求添加标头,然后呈现响应:

request.env['HTTP_ACCEPT_ENCODING'] = 'gzip'
render :json => response.to_json()

1
确认 @curiousmind 在下面的回复中所说的 - 你还必须添加 Rack::Deflater 中间件才能让 Rails 压缩 JSON 响应,即使你已经将 Accept-Encoding 请求头设置为 'gzip,deflate'。请注意,压缩需要请求头的支持。 - sameers

5

您可以设置自定义头来查询Curl以获取经过gzip压缩的响应。

$ curl -H "Accept-Encoding: gzip, deflate" localhost:3000/posts.json > posts_json.gz

然后,解压缩它以查看实际的响应JSON。
 $ gzip -d posts_json.gz
 $ cat posts_json

如果它不起作用,请回帖并附上rake中间件的输出,以帮助我们进一步排除故障。


4
除了添加标题外,我还需要在应用程序的application.rb文件中添加“config.middleware.use Rack::Deflater”,以便实际压缩文件。 - Nate Bird

1
在某些情况下,您可以考虑将大量响应写入文件并进行gzip压缩:
res = {} # huge data hash
json = res.to_json

Zlib::GzipWriter.open('public/api/huge_data.json.gz') { |gz| gz.write json }

并定期更新此文件


压缩功能正常,但如何将这些数据作为API响应发送给客户端?提前致谢。 - QuadSquad

1
考虑在使用Rails时不要将Rack中间件放在config.ru文件中。
Rails 2以来,Rails就有了自己的中间件栈管理器。
正确的方法是:
# config/application.rb or config/environment.rb depends on your Rails version
config.middleware.use Rack::Deflater

不要在使用 Rack::ETag 时使用 @djcp 的解决方案。
简短回答:
module MyApp
  class Application < Rails::Application
    config.middleware.insert_before Rack::ETag, Rack::Deflater
  end
end

"Rack::DeflaterRack::ETag的顺序很重要,因为Rack::Deflater使用Zlib::GzipWriter来压缩响应正文,默认情况下会带有时间戳,这意味着即使原始响应正文相同,压缩后的响应正文每秒都会改变。

要复现此问题,请运行以下脚本:

"
require 'rack/etag'
require 'rack/deflater'
require 'rack/content_length'

@app = Rack::Builder.new do
  use Rack::ETag
  use Rack::Deflater
  use Rack::ContentLength
  run ->(*) { [200, {}, ['hello world']] }
end

def puts_etag
  puts @app.call({ 'HTTP_ACCEPT_ENCODING' => 'gzip' })[1]['ETag']
end

puts_etag
sleep 1
puts_etag

可以简单地交换 Rack::ETagRack::Deflater 的行,以获得预期的输出。 Rails 默认使用 Rack::ETag,而 config.middleware.use 只是追加。要在 Rack::Etag 之前插入 Rack::Deflater,请改用 config.middleware.insert_before


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接