9

在使用方法和时,告诉 Rails 使用弱而不是强 ETAG的最佳方法是什么?fresh_whenstale?

我问的原因是nginx (正确地)在启用动态 gzipping 时从响应中删除了强大的 ETAG 标头

4

4 回答 4

7

我从@grosser 的答案中获取了代码并将其变成了一个宝石:

您可以将其添加到您的 gemfile 中:

gem 'rails_weak_etags'

它将在之前安装到您的中间件中Rack::ConditionalGet

> bundle exec rake middleware
....
use RailsWeakEtags::Middleware
use Rack::ConditionalGet
use Rack::ETag
....

然后所有由 rails 生成的电子标签,无论是使用 Rack::ETag 还是使用显式电子标签,都将转换为弱标签。使用修补过的或版本 > 1.7.3 的 nginx,将允许您使用电子标签和 gzip 压缩。

RACK 1.6 默认 etags 为弱- 如果您升级,此 gem 将不再有用。

于 2014-07-10T16:53:34.460 回答
3

中间件:

class WeakEtagMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    # make request etags "strong"
    etag = env['HTTP_IF_NONE_MATCH']
    if etag && etag =~ /^W\//
      env['HTTP_IF_NONE_MATCH'] = etag[2..-1]
    end

    status, headers, body = @app.call(env)

    # make response etags "weak"
    etag = headers['ETag']
    if etag && etag !~ /^W\//
      headers['ETag'] = "W/#{etag}"
    end

    [status, headers, body]
  end
end

加上添加中间件

Rails.application.config.middleware.insert_before(Rack::ETag, WeakEtagMiddleware)

加上单元测试

context WeakEtagMiddleware do
  let(:backend) { Rack::ConditionalGet.new(Rack::ETag.new(lambda { |env| [env["status"] || 200, {}, ["XXX"]] })) }
  let(:app) { WeakEtagMiddleware.new(backend) }
  let(:expected_digest_1) { "bc9189406be84ec297464a514221406d" }
  let(:env) { {"REQUEST_METHOD" => "GET"} }

  should "converts etags to weak" do
    status, headers, body = app.call(env)
    assert_equal %{W/"#{expected_digest_1}"}, headers["ETag"]
    assert_equal status, 200
  end

  should "not add etags to responses without etag" do
    status, headers, body = app.call(env.merge("status" => 400))
    refute headers["ETag"]
    assert_equal status, 400
  end

  should "recognize weak ETags" do
    status, headers, body = app.call(env.merge("HTTP_IF_NONE_MATCH" => %{W/"#{expected_digest_1}"}))
    assert_equal status, 304
  end

  should "not recognize invalid ETags" do
    status, headers, body = app.call(env.merge("HTTP_IF_NONE_MATCH" => %{W/"something-not-fresh"}))
    assert_equal status, 200
  end
end

加上集成测试

require_relative "../helpers/test_helper"

class WeakEtagsTest < ActionController::IntegrationTest
  class TestController < ActionController::Base
    def auto
      render :text => "XXX"
    end

    def fresh
      if stale? :etag => "YYY"
        render :text => "XXX"
      end
    end
  end

  additional_routes do
    get '/test/weak_etags/:action', :controller => 'weak_etags_test/test'
  end

  fixtures :accounts, :users

  context "weak etags" do
    let(:expected_digest_1) { "bc9189406be84ec297464a514221406d" }
    let(:expected_digest_2) { "fd7c5c4fdaa97163ee4ba8842baa537a" }

    should "auto adds weak etags" do
      get "/test/weak_etags/auto"
      assert_equal "XXX", @response.body
      assert_equal %{W/"#{expected_digest_1}"}, @response.headers["ETag"]
    end

    should "adds weak etags through fresh_when" do
      get "/test/weak_etags/fresh"
      assert_equal "XXX", @response.body
      assert_equal %{W/"#{expected_digest_2}"}, @response.headers["ETag"]
    end

    should "recognize auto-added ETags" do
      get "/test/weak_etags/auto", {}, {"HTTP_IF_NONE_MATCH" => %{W/"#{expected_digest_1}"}}
      assert_response :not_modified
    end

    should "recognize fresh ETags" do
      get "/test/weak_etags/fresh", {}, {"HTTP_IF_NONE_MATCH" => %{W/"#{expected_digest_2}"}}
      assert_response :not_modified
    end
  end
end
于 2014-01-08T18:24:43.273 回答
1

看起来 Rack::ETag 将来会使用weak-etags:

于 2014-08-18T17:54:59.980 回答
1

这是避免在应用程序服务器中进行任何更改的替代方法。该指令将应用程序返回的所有 etag 转换为弱 etag,然后再从响应中删除它们。把它放在你的 nginx 配置里面:

location / {
  add_header ETag "W/$sent_http_ETAG";
}

我已经检查过这适用于 nginx 1.7.6。

于 2014-10-23T05:33:33.213 回答