在使用方法fresh_when
和stale?
时告诉rails使用weak instead of strong ETAGs的最佳方法是什么?
我问的原因是nginx (correctly) removes strong ETAG headers from responses when on-the-fly gzipping is enabled。
答案 0 :(得分:6)
我从@ grosser的答案中取出代码并将其变成了宝石:
您可以将它添加到您的gemfile:
gem 'rails_weak_etags'
它将在Rack::ConditionalGet
之前安装到您的中间件中:
> bundle exec rake middleware
....
use RailsWeakEtags::Middleware
use Rack::ConditionalGet
use Rack::ETag
....
然后,使用Rack :: ETag或带有显式电子标签的rails生成的所有电子标签都将转换为弱标签。使用修补或版本> 1.7.3 of nginx,然后让你使用电子标签和gzip压缩。
RACK 1.6默认etags为弱 - 如果升级,此gem不再有用。
答案 1 :(得分: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
答案 2 :(得分:1)
看起来Rack :: ETag将来会使用弱电子标签:
答案 3 :(得分:1)
这是一种可以避免在应用程序服务器中进行任何更改的替代方法。该指令将应用程序返回的所有etags转换为弱etags,然后才从响应中删除它们。把它放在你的nginx配置里面:
location / {
add_header ETag "W/$sent_http_ETAG";
}
我已经检查过这适用于nginx 1.7.6。