codewiser/http-cache-control

Laravel 的 HTTP 缓存控制层

v2.1.7 2024-08-30 09:52 UTC

This package is auto-updated.

Last update: 2024-09-30 09:55:22 UTC


README

此包提供与 HTTP Cache-Control 标头一起工作的解决方案。资源被缓存,然后在 Eloquent 事件上失效。服务器可以不进行数据库请求,仅使用缓存值来响应。

准备模型

CacheControl 使用缓存来保存标头值。然后模型发生变化时,相关的缓存必须失效。

模型必须实现 \Codewiser\HttpCacheControl\Contracts\Cacheable

以下是一个实现示例。类共享缓存标签,因此更改任何模型将使共享缓存失效。

use Codewiser\HttpCacheControl\Contracts\Cacheable;
use Codewiser\HttpCacheControl\Observers\InvalidatesCache;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Psr\SimpleCache\CacheInterface;

#[ObservedBy(InvalidatesCache::class)]
class User extends Model implements Cacheable
{
    public function cache(): CacheInterface
    {
        return Cache::tags(['user', 'order']);
    }
}

#[ObservedBy(InvalidatesCache::class)]
class Order extends Model implements Cacheable
{
    public function cache(): CacheInterface
    {
        return Cache::tags(['order', 'user']);
    }
}

用法

CacheControl 类分析请求标头并创建带有适当标头的响应。

第一个参数必须是一个 \Psr\SimpleCache\CacheInterface\Codewiser\HttpCacheControl\Contracts\Cacheable 的类名,或者实现该接口的模型类。

第二个参数是一个回调,它应该返回响应内容。此回调仅在必要时才会被调用。

Cache-Control 标头

您可以设置您喜欢的任何 Cache-Control 指令

use Codewiser\HttpCacheControl\CacheControl;
use Codewiser\HttpCacheControl\CacheControlHeader;

public function index(Request $request)
{
    return CacheControl::make(Order::class, function() {
        return OrderResource::collection(Order::all())
    })
        ->cacheControl(fn(Request $request) => new CacheControlHeader(
            public: true,
            max_age: 1800,
            must_revalidate: true,
        ));
}

如果没有设置 publicprivate 指令,则默认为 private

在这种情况下,服务器上不会缓存任何内容。

Expires 标头

或者您可以只设置 Expires 标头

use Codewiser\HttpCacheControl\CacheControl;
use Codewiser\HttpCacheControl\CacheControlHeader;

public function index(Request $request)
{
    return CacheControl::make(Order::class, function() {
        return OrderResource::collection(Order::all())
    })
        ->expires(now()->addHour());
}

在这种情况下,服务器上不会缓存任何内容。

缓存整个响应

您可能希望缓存整个响应

use Codewiser\HttpCacheControl\CacheControl;
use Codewiser\HttpCacheControl\CacheControlHeader;

public function index(Request $request)
{
    return CacheControl::make(Order::class, function() {
        return OrderResource::collection(Order::all())
    })
        ->remember()
        ->cacheControl(new CacheControlHeader(
            public: true,
            max_age: now()->addHour(),
            must_revalidate: true,
        ));
}

请谨慎使用。注意,缓存可能会变得很大。

私有缓存

如果控制器响应不应在用户之间共享,您应该设置 Cache-Control: private 指令。

use Codewiser\HttpCacheControl\CacheControl;
use Codewiser\HttpCacheControl\CacheControlHeader;

public function index(Request $request)
{
    return CacheControl::make(Order::class, function(Request $request) {
        return OrderResource::collection(Order::all())
            ->whereBelongsTo($request->user()
    })
        ->cacheControl(new CacheControlHeader(
            private: true,
            max_age: new DateInterval('PT1H'),
            must_revalidate: true,
        ));
}

Vary 标头

Vary 标头描述了一组对缓存有影响的请求标头。

例如,您的应用程序支持不同的语言,因此缓存取决于 Accept-Language 请求标头

use Codewiser\HttpCacheControl\CacheControl;
use Codewiser\HttpCacheControl\CacheControlHeader;

public function index(Request $request)
{
    return CacheControl::make(Order::class, function() {
        return OrderResource::collection(Order::all())
    })
        ->vary('Accept-Language')
        ->cacheControl(new CacheControlHeader(
            public: true,
            max_age: 1800,
            must_revalidate: true,
        ));
}

请注意,Web 服务器可能会附加更多的 Vary 标头值。通常为 Accept-Encoding

条件标头

控制器可以响应 ETag 和/或 Last-Modified 标头,因此下一个请求可以带有 If-None-MatchIf-Modified-Since 标头,这使得它成为条件性的。

use Codewiser\HttpCacheControl\CacheControl;
use Codewiser\HttpCacheControl\CacheControlHeader;

public function index(Request $request)
{
    return CacheControl::make(Order::class, function() {
        return OrderResource::collection(Order::all())
    })
        ->cacheControl(['public' => true])
        // Implicit
        ->etag()
        // Or explicit
        ->etag(fn() => custom_etag_calculation(Order::all()));
}

在此示例中,本地缓存仅存储 ETag 值。这足以检查未来的请求中的 If-None-Match

这种方式是最节省缓存的。

use Codewiser\HttpCacheControl\CacheControl;
use Codewiser\HttpCacheControl\CacheControlHeader;

public function index(Request $request)
{
    return CacheControl::make(Order::class, function() {
        return OrderResource::collection(Order::all())
    })
        ->cacheControl(['public' => true])
        // Return timestamp or DateTimeInterface
        ->lastModified(fn() => Order::all()->max('updated_at'));
}