Laravel 中间件 SetCacheHeaders (etag/no-cache/max-age)
有一个中间件在 Laravel 中没有受到关注。 它称为SetCacheHeaders
,别名为cache.headers
。 而且,文档中没有提到它。 我不是在开玩笑,伙计们:
对中间件的简单一瞥告诉我们可以使用它来将缓存标头添加到响应中, 为我们节省了创建我们自己的中间件的工作。
在响应中缓存?你的意思是?
如果你这么问,就意味着你不知道什么是 HTTP 缓存控制指令。
简而言之,缓存控制是一种告诉浏览器您的应用程序发送的响应是否应该被缓存以及在什么条件下缓存的方法,以便它可以决定它是否应该再次接收完整的响应。
这是一种非常以浏览器为中心的机制,稍后您将看到原因。 Google Developers 中有一篇 非常好的文章 它将教你如何工作,你绝对应该阅读。我将等待。
准备好? 现在我们知道了 HTTP 缓存控制 的工作原理,我们可以理解这个中间件试图实现的目标。 或者跳过下一部分的 有用 部分。
SetCacheHeaders 中间件如何工作?
让我们检查 源代码。
<?php
namespace Illuminate\Http\Middleware;
use Closure;
class SetCacheHeaders
{
/**
* 添加缓存相关的 HTTP 标头
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|array $options
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \InvalidArgumentException
*/
public function handle($request, Closure $next, $options = [])
{
$response = $next($request);
if (! $request->isMethodCacheable() || ! $response->getContent()) {
return $response;
}
if (is_string($options)) {
$options = $this->parseOptions($options);
}
if (isset($options['etag']) && $options['etag'] === true) {
$options['etag'] = md5($response->getContent());
}
$response->setCache($options);
$response->isNotModified($request);
return $response;
}
/**
* 解析给定的标头选项
*
* @param string $options
* @返回数组
*/
protected function parseOptions($options)
{
return collect(explode(';', $options))->mapWithKeys(function ($option) {
$data = explode('=', $option, 2);
return [$data[0] => $data[1] ?? true];
})->all();
}
}
如您所见,此中间件的 handle()
方法中有五个主要语句。试图用语言表达每个人的作用:
- 首先检查请求是
GET
还是HEAD
。你不能缓存POST
响应,因为这个方法意味着 改变了 的东西。 - 检查您是否向中间件传递了参数,并使用
parseOptions()
方法将它们解析为响应标头。 - 如果设置了
etag
选项,它会自动对响应内容进行 hash 处理,以便可以快速将其与请求发送的etag
进行比较。 - 它将在 Response 标头中设置
Cache-Control
选项。 - 最后,它会 检查响应是否未被修改。如果不是,它将删除内容并只返回
etag
,从而节省宝贵的字节。
重点是第三点和第五点。如果原始响应是带有缓存响应的 etag
,浏览器将向应用程序发出请求,你的应用程序将接收 etag
,处理整个请求,最后在响应的 etag
中再次对内容进行散列。通过比较两个 etag
,应用程序可以知道是否应该再次发送整个响应,或者只发送告诉浏览器内容没有更改的同一个 etag
。
优雅的 etag
如何在这个应用程序中工作。
强调“处理整个请求”是其中的关键部分。应用程序或浏览器不会知道内容是否已经改变,除非整个响应已经准备就绪,这意味着: 无论如何,你所有的应用程序逻辑将从开始运行到结束。如果您需要更积极的缓存,您可以将目光投向了不起的 空间响应缓存.
就我个人而言,我认为 Cache Control 是一个利基头部。如果我想实现这种技术,最好缓存服务器中的响应,然后添加 etag 以节省两端的时间。
使用 SetCacheHeaders 中间件
既然我们知道了这个中间件的作用,那么就很容易使用它了,让我们用一个简单的现实世界的例子,比如我的永远的博客应用程序。
在我的博客应用程序中,主页上有大量最新发布的博客列表,根据用户认证和订阅的博客不同而有所不同。我的指标是,每个出版物之间至少有5分钟的间隔。
让我们看看谷歌的 流程图以及应该使用什么样的缓存控制策略:
谷歌开发者 — Google (2019)
好了,我们明白了:
no-cache
及etag
将允许用户刷新页面(甚至在过期时间之前),而不下载整个页面,如果最新发布的博客任然是相同的。private
就是说,首页是每个用户,应该只缓存在用户设备,而不是,例如,一个代理。
,首页是每个用户,应该只缓存在用户设备,而不是,例如,一个代理max-age=300
将设置一个5分钟的过期时间,因为在这之后肯定会有一个新的博客发行。
Route::get('/', 'PodcastController@index')
->middleware('cache.headers:no-cache,private,max-age=300;etag');
仅此而已。没有必要创建自己的中间件或者入侵 Apache 或 NGINX,这是开箱即用的。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。