【问题标题】:rails json response with gzip compression使用 gzip 压缩的 rails json 响应
【发布时间】:2013-05-21 22:29:49
【问题描述】:

我有一个用 rails 编写的 api,它在每个请求上都会以 JSON 响应进行响应。

响应可能很大,所以我需要使用 gzip 压缩 JSON 响应。

想知道如何在 Rails 控制器中执行此操作?

我已经添加了行

use Rack::Deflater

在 config.ru 中

我是否也应该更改呈现 JSON 的行中的某些内容?

render :json => response.to_json()

另外,我如何检查响应是否为 gzip 格式..?

我从终端做了一个 curl 请求,我只看到普通的纯 JSON。

【问题讨论】:

    标签: ruby-on-rails json ruby-on-rails-3 gzip rack


    【解决方案1】:

    我的帖子Content Compression with Rack::Deflater 描述了几种集成 Rack::Deflater 的方法。最简单的方法是将config/application.rb 更新为:

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

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

    【讨论】:

    • 我需要这个机架中间件声明以及标题来获得 gzip 响应。谢谢!
    • 我将 config.middleware.use Rack::Deflater 添加到 application.rb,压缩仅在 Mac OS(Chrome、FF 和 Safary)上开始工作。在 Windows 上,我收到没有“内容编码:gzip”的响应;在 FF 和 IE 中(仅适用于 chrome)。附言“Accept-Encoding: gzip”出现在所有浏览器中。你知道为什么吗?)
    • @bmalets 这还会发生吗?找到解决方法了吗?
    • @ChristianFazzini,我记得,这是旧的 FF 和 IE 浏览器的问题,我通过在 nginx 配置中启用 gzipping 解决了这个问题
    • @bmalets 你还记得你提到的FF和IE的版本吗?关于在 nginx 配置中添加 gzipping。是不是类似于mattstauffer.co/blog/…
    【解决方案2】:

    对于 gzip 格式的响应,我们不必更改 render 方法调用。
    如果请求具有标头 Accept-Encoding: gzip,Rails 将使用 gzip 自动压缩 JSON 响应。

    如果您不希望用户发送带有预设标头的请求。您可以在呈现响应之前在控制器中手动将标头添加到请求中:

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

    【讨论】:

    • 在下方确认 @curiousmind 的响应 - 您还必须添加 Rack::Deflater 中间件以让 Rails 压缩 JSON 响应,即使您已通过 Accept-Encoding 请求标头设置为“gzip,deflate”。请注意,压缩也需要请求标头。
    【解决方案3】:

    您可以通过设置自定义标头来查询 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 middlewares 的输出,以帮助我们进一步排除故障。

    【讨论】:

    • 除了标题之外,我还必须将 'config.middleware.use Rack::Deflater' 添加到应用程序的 application.rb 文件中,以便实际压缩文件。
    【解决方案4】:

    在某些情况下,您可以考虑将大量响应写入文件并 gzip:

    res = {} # huge data hash
    json = res.to_json
    
    Zlib::GzipWriter.open('public/api/huge_data.json.gz') { |gz| gz.write json }
    

    并定期更新此文件

    【讨论】:

    • 压缩工作正常,但如何将此数据作为 api 响应发送给客户端。提前致谢
    【解决方案5】:

    考虑在使用 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 to compress the response body 并且默认情况下会使用时间戳进行压缩,这意味着即使原始响应正文是一样。

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

    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 uses Rack::ETag by defaultconfig.middleware.use 只是追加。要在Rack::Etag 之前插入Rack::Deflater,请改用config.middleware.insert_before

    ?

    【讨论】:

      猜你喜欢
      • 2012-02-12
      • 1970-01-01
      • 2014-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多