ETag根据每个请求进行更改。机架中间件干扰?

时间:2014-02-14 09:35:42

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2 rack etag

为了改进我的Rails(3.2)应用程序中的缓存,我发现每个请求都会改变我的Etag标题。

我想这是因为我的中间件的某些部分正在改变每个请求的响应内容,但我不明白是什么。这是运行rake middleware

的结果
use Rack::Cache
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007f462e790e98>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use ActionDispatch::Head
use Rack::ConditionalGet
use Rack::Deflater
use Rack::ETag
use ActionDispatch::BestStandardsSupport
use Warden::Manager
use Rack::Attack
use Rack::SslEnforcer
use HireFire::Middleware`

经过一番研究后,我偶然发现了一些信息,即Rack :: Deflater正在为其缩减的所有内容生成时间戳。我尝试在Rack :: ETag(config.middleware.insert_after Rack::ETag, Rack::Deflater)之后移动Rack :: Deflater,但ETag标头仍然在每次请求时都会发生变化。

这里有更多机架经验的人可以帮我找出造成这种行为的原因吗?

1 个答案:

答案 0 :(得分:5)

以下是Rack::ETag的相关源代码:

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

  if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
    original_body = body
    digest, new_body = digest_body(body)
    body = Rack::BodyProxy.new(new_body) do
      original_body.close if original_body.respond_to?(:close)
    end
    headers['ETag'] = %("#{digest}") if digest
  end

  unless headers['Cache-Control']
    if digest
      headers['Cache-Control'] = @cache_control if @cache_control
    else
      headers['Cache-Control'] = @no_cache_control if @no_cache_control
    end
  end

  [status, headers, body]
end

如您所见,摘要是根据响应正文计算的(使用digest_body)。这是来源:

def digest_body(body)
  parts = []
  digest = nil

  body.each do |part|
    parts << part
    (digest ||= Digest::MD5.new) << part unless part.empty?
  end

  [digest && digest.hexdigest, parts]
end

你可以挂钩该方法。我相信您会发现您的回复中存在动态内容,这会阻止您使用ETag标头。例如,您的表单中可能存在CSRF保护令牌,或者类似的东西。我强烈建议您使用debuggerpry之类的东西来放置断点。