本文目录导读:

是的,在 Laravel 中通常使用中间件来设置缓存头(Cache-Control、Expires 等),这是最优雅、最符合 Laravel 设计模式的做法。
为什么用中间件?
- 关注点分离:缓存策略属于 HTTP 响应头处理,不应写在控制器业务逻辑里
- 可复用:多个路由/控制器可以共用同一个缓存策略
- 灵活控制:可以针对不同路由设置不同的缓存时长
- 条件判断:可以根据用户登录状态、环境等动态决定是否缓存
实现方式
自定义中间件
// app/Http/Middleware/SetCacheHeaders.php
namespace App\Http\Middleware;
use Closure;
use Symfony\Component\HttpFoundation\Response;
class SetCacheHeaders
{
public function handle($request, Closure $next, $maxAge = 3600)
{
/** @var Response $response */
$response = $next($request);
// 只缓存 GET 请求
if (!$request->isMethod('GET')) {
return $response;
}
// 设置缓存头
$response->headers->set('Cache-Control', sprintf(
'public, max-age=%d, s-maxage=%d',
$maxAge,
$maxAge * 2 // CDN 可以缓存更久
));
// 设置 Expires 头
$response->headers->set(
'Expires',
gmdate('D, d M Y H:i:s', time() + $maxAge) . ' GMT'
);
return $response;
}
}
注册中间件
// app/Http/Kernel.php
protected $routeMiddleware = [
// ... 其他中间件
'cache.headers' => \App\Http\Middleware\SetCacheHeaders::class,
];
在路由中使用
// 方式一:在路由定义中指定
Route::middleware('cache.headers:3600')->group(function () {
Route::get('/articles', [ArticleController::class, 'index']);
Route::get('/articles/{id}', [ArticleController::class, 'show']);
});
// 方式二:在控制器中使用
class ArticleController extends Controller
{
public function __construct()
{
// 对指定方法应用缓存
$this->middleware('cache.headers:300')->only(['index', 'show']);
}
}
更高级的方案:使用缓存标签
如果需要对不同资源设置不同缓存策略,可以使用中间件参数:
// 自定义中间件支持多个标签
class SetCacheHeaders
{
public function handle($request, Closure $next, ...$params)
{
$response = $next($request);
// 解析参数
$maxAge = 3600;
$tags = [];
foreach ($params as $param) {
if (is_numeric($param)) {
$maxAge = (int) $param;
} else {
$tags[] = $param;
}
}
// 可以根据 tags 做更精细的控制
// 不同的标签代表不同的内容类型
if (in_array('user-specific', $tags) && auth()->check()) {
// 用户私有内容,加 private 缓存
$response->headers->set('Cache-Control', 'private, max-age=60');
} else {
$response->headers->set('Cache-Control', sprintf(
'public, max-age=%d', $maxAge
));
}
return $response;
}
}
// 路由使用
Route::middleware('cache.headers:300,public,articles')->get('/api/articles', ...);
内置中间件:CacheControl 和 SetCacheHeaders
Laravel 本身也提供了一些相关中间件:
cache.headers(Laravel 自带)
// Laravel 已经有一个内置的 cache.headers 中间件
// 用法略有不同
Route::get('/articles', function () {
return response()->json($articles);
})->middleware('cache.headers:public;max_age=3600;etag');
或者使用 response()->withHeaders()
// 在控制器中
return response()
->json($data)
->withHeaders([
'Cache-Control' => 'public, max-age=3600',
'ETag' => md5(serialize($data)),
]);
实践建议
- GET 请求:通常需要缓存(公共资源 3600s,用户私有资源 60s)
- :使用 ETag + 304 Not Modified
- API 接口:建议设置短缓存(如 60-300 秒)
- 静态资源:由 Nginx/CDN 处理,Laravel 不介入
- 登录用户:区分
public和private缓存
完整示例:条件缓存中间件
class ConditionalCacheHeaders
{
public function handle($request, Closure $next, $publicMaxAge = 3600)
{
$response = $next($request);
// 只缓存成功的 GET 响应
if ($request->isMethod('GET') && $response->isSuccessful()) {
// 如果是登录用户,使用 private 缓存
$cacheType = auth()->check() ? 'private' : 'public';
// 动态计算 max-age
$maxAge = auth()->check() ? 60 : $publicMaxAge;
$response->headers->set('Cache-Control', sprintf(
'%s, max-age=%d',
$cacheType,
$maxAge
));
// 添加 ETag 支持条件请求
$response->headers->set('ETag', '"' . md5($response->getContent()) . '"');
}
return $response;
}
}
是的,强烈推荐使用中间件,把缓存策略作为横切关注点集中管理,让你的控制器保持干净,专注于业务逻辑。