Laravel缓存头用中间件吗

wen PHP项目 2

本文目录导读:

Laravel缓存头用中间件吗

  1. 为什么用中间件?
  2. 实现方式
  3. 更高级的方案:使用缓存标签
  4. 内置中间件:CacheControl 和 SetCacheHeaders
  5. 实践建议
  6. 完整示例:条件缓存中间件

是的,在 Laravel 中通常使用中间件来设置缓存头(Cache-Control、Expires 等),这是最优雅、最符合 Laravel 设计模式的做法。

为什么用中间件?

  1. 关注点分离:缓存策略属于 HTTP 响应头处理,不应写在控制器业务逻辑里
  2. 可复用:多个路由/控制器可以共用同一个缓存策略
  3. 灵活控制:可以针对不同路由设置不同的缓存时长
  4. 条件判断:可以根据用户登录状态、环境等动态决定是否缓存

实现方式

自定义中间件

// 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)),
    ]);

实践建议

  1. GET 请求:通常需要缓存(公共资源 3600s,用户私有资源 60s)
  2. :使用 ETag + 304 Not Modified
  3. API 接口:建议设置短缓存(如 60-300 秒)
  4. 静态资源:由 Nginx/CDN 处理,Laravel 不介入
  5. 登录用户:区分 publicprivate 缓存

完整示例:条件缓存中间件

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;
    }
}

是的,强烈推荐使用中间件,把缓存策略作为横切关注点集中管理,让你的控制器保持干净,专注于业务逻辑。

抱歉,评论功能暂时关闭!