本文以 Etag 方式为例,介绍304设置:

什么是“ETag”?

Etag 是一种标识,一般附带在响应头部中,值是页面内容的哈希值,用来判断资源(页面,json,xml)有没有修改,如果没有修改,就返回 304 状态码,有修改则生成新的 Etag 值。
浏览器根据状态码判断是否缓存过期。
服务器生成 Etag,和客户端保存 Etag ,并在请求中附带 Etag 的过程如下:

  1. 客户端请求一个页面(A)。
  2. 服务器返回页面A,并在给A加上一个ETag。
  3. 客户端展现该页面,并将页面连同ETag一起缓存。
  4. 客户再次请求页面A,并将上次请求时服务器返回的ETag一起传递给服务器。
  5. 服务器检查该ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304(未修改——Not Modified)和一个空的响应体。

    用法示例

    在典型用法中,当一个URL被请求,Web服务器会返回资源和其相应的ETag值,它会被放置在HTTP的“ETag”字段中:
    ETag: “686897696a7c876b7e”
    然后,客户端可以决定是否缓存这个资源和它的ETag。以后,如果客户端想再次请求相同的URL,将会发送一个包含已保存的ETag和“If-None-Match”字段的请求。
    If-None-Match: “686897696a7c876b7e”
    客户端请求之后,服务器可能会比较客户端的ETag和当前版本资源的ETag。如果ETag值匹配,这就意味着资源没有改变,服务器便会发送回一个极短的响应,包含HTTP “304 未修改”的状态。304状态告诉客户端,它的缓存版本是最新的,并应该使用它。
    然而,如果ETag的值不匹配,这就意味着资源很可能发生了变化,那么,一个完整的响应就会被返回,包括资源的内容,就好像ETag没有被使用。这种情况下,客户端可以用新返回的资源和新的ETag替代先前的缓存版本。

Lumen中的使用

在lumen框架中可以通过后置中间件的形式给接口统一设置304

实例:

cache304 中间件

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. use Illuminate\Http\Request;
  5. class CacheMiddleware
  6. {
  7. /**
  8. * Handle an incoming request.
  9. *
  10. * @param Request $request
  11. * @param \Closure $next
  12. * @return mixed
  13. */
  14. public function handle($request, Closure $next)
  15. {
  16. $response = $next($request);
  17. $etag = md5($response->getContent());
  18. $requestETag = str_replace('"', '', $request->getETags());
  19. if ($requestETag && $requestETag[0] == $etag) {
  20. // Modifies the response so that it conforms to the rules defined for a 304 status code.
  21. $response->setNotModified();
  22. }
  23. $response->setETag($etag);
  24. # 其他设置点可以看源码
  25. # return $response->setMaxAge(30);
  26. return $response;
  27. }
  28. }

中间件注册

  1. $app->routeMiddleware([
  2. 'cache304' => App\Http\Middleware\CacheMiddleware::class,
  3. ]);

路由引用

  1. $router->get('/api/getTrack', ['middleware' => ['jwt', 'cache304'], 'uses' => 'TrackController@getTrack']);

注意:nginx开启gzip模块后ETAG丢失问题

上述设置要检查是否Nginx开启gzip模块导致ETAG丢失,若丢失可以设置etag为弱etag

Lumen设置etag方法如下:

  1. /**
  2. * Sets the ETag value.
  3. *
  4. * @param string|null $etag The ETag unique identifier or null to remove the header
  5. * @param bool $weak Whether you want a weak ETag or not
  6. *
  7. * @return $this
  8. *
  9. * @final
  10. */
  11. public function setEtag(string $etag = null, bool $weak = false): object
  12. {
  13. if (null === $etag) {
  14. $this->headers->remove('Etag');
  15. } else {
  16. if (0 !== strpos($etag, '"')) {
  17. $etag = '"'.$etag.'"';
  18. }
  19. $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
  20. }
  21. return $this;
  22. }

设置弱etag方法:

  1. $response->setETag($etag, true);