在我的基于rack的应用程序中,我想服务CSS和JS,所以我使用Rack::Static
中间件,如下所示:
config.ru
use Rack::Static, urls: ["/css" ], root: "public"
run MyApp
public
文件夹结构:
public
css
application.min.css
根据https://github.com/rack/rack/blob/2.2.4/lib/rack/static.rb的Rack::Static
实现(链接指的是我使用的机架版本中的代码,即2.2.4),默认情况下,Cache-Control
头将不设置在响应。
但是如果我使用下面的配置
use Rack::Static, urls: ["/css" ], root: "public",
:header_rules => [
# Cache CSS/JS files, matching given regex in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.1.2.1.css
#
[ /.(?:[1-9].[0-9].[0-9]).(?:css|js)z/, {'cache-Control' => 'public, max-age=60'} ]
]
然后我可以在响应头下看到以下标题Cache-Control: public, max-age=60
,例如在Firefox的Web开发人员工具下的网络选项卡中。
现在我想使用指纹策略缓存该CSS文件,如下面的参考资料所示,我找到了
https://css-tricks.com/strategies-for-cache-busting-css/aa-changing-file-name
https://csswizardry.com/2019/03/cache-control-for-civilians/
所以在我的HTML页面中,我要让我的样式表名称包含指纹版本,例如如下
<head>
...
...
<link href="/css/application.min.<MY_ASSET_VERSION>.css" rel="stylesheet">
</head>
设置<MY_ASSET_VERSION>
为1.0.0
。
但是我不应该在我的public
文件夹中有任何名为application.min.1.0.0.css
的文件。这样命名只是为了触发缓存崩溃。那么我怎么做Rack::Static
当文件css/application.min.css
遇到路径/css/application.min.1.0.0.css
时,为其提供服务?
我是否需要实现一个中间件,应该放在应用程序的中间件堆栈后Rack::Static
?如果是,谁能帮我一个例子,因为我没有实现任何中间件。
或者,如果有其他标准的方法来满足手头的需求,请提出建议。
谢谢。
张贴下面的解决方案,我实现使用中间件,这是为我工作。
中间件)/custom_middleware util.rb
module CustomMiddleware
module Util
extend self
EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED = /css|js/
ASSET_FINGER_PRINT_FORMAT_REGEX = /[1-9].[0-9].[0-9]/
FINGER_PRINTED_ASSET_NAME_MATCHER_REGEX = /.(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX}).(?:#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})z/
ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX = /(.+).(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX}).(#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})z/
def determine_original_asset_name(fingerprinted_asset_name:)
md = fingerprinted_asset_name.match(ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX)
return fingerprinted_asset_name if md.nil?
arr = md.captures
asset_file_name = arr[0]
asset_file_extension = arr[1]
asset_name = "#{asset_file_name}.#{asset_file_extension}"
asset_name
end
end
end
中间件)/custom_middleware fingerprinted_asset_name_modifier.rb
require_relative 'util'
module CustomMiddleware
class FingeprintedAssetNameModifier
def initialize(app)
@app = app
end
def call(env)
env_path_info_key = 'PATH_INFO'
orig_path = env[env_path_info_key]
modified_path = Util.determine_original_asset_name(fingerprinted_asset_name: orig_path)
if modified_path != orig_path
env.merge!(env_path_info_key => modified_path)
end
@app.call(env)
end
end
end
config.ru
require_relative "middlewares/custom_middleware/fingerprinted_asset_name_modifier"
use CustomMiddleware::FingeprintedAssetNameModifier
use Rack::Static, urls: ["/css", "/js" ], root: "public",
:header_rules => [
# Cache CSS/JS files in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.css
[ %w(css js), {'cache-control' => 'public, max-age=60'} ]
]
run MyApp
当下列CSS文件包含在我的页面
时,使用上述解决方案<head>
...
...
<link href="/css/application.min.1.0.0.css" rel="stylesheet">
</head>
application.min.1.0.0.css
文件服务于我的文件在public/css/application.min.css
和响应头Cache-Control: public, max-age=60
设置意味着60秒后,如果application.min.1.0.0.css
被重新请求,它将从我的应用程序服务,而不是从浏览器的缓存。
如果更改页面中的资产指纹,也在第一次请求资产的60秒内,如以下
<link href="/css/application.min.1.0.5.css" rel="stylesheet">
和重新加载页面,资源从我的应用程序提供,而不是从浏览器的缓存。
希望这对那些可能会提出类似问题帖子的需求的人有用。
谢谢。