webfactory/http-cache-bundle

Symfony 扩展包,通过最后修改时间头简化 HTTP 缓存验证

2.0.0 2024-03-28 18:29 UTC

This package is auto-updated.

Last update: 2024-08-27 07:37:30 UTC


README

WebfactoryHttpCacheBundle 是一个 Symfony 扩展包,它提供了一个比 symfony/http-kernel 包中的 #[Cache] 属性 更强大的功能,用于通过最后修改时间头进行 HTTP 缓存验证。

#[ReplaceWithNotModifiedResponse] 属性允许您为请求页面的每个底层资源编写小的 LastModifiedDeterminators。它们可以自由地重复使用和组合,甚至可以定义为服务。

考虑以下控制器代码

<?php

// ...
use Webfactory\HttpCacheBundle\NotModified\Attribute\ReplaceWithNotModifiedResponse;

class MyController {
    // Routing etc. configuration skipped for brevity
     
    #[ReplaceWithNotModifiedResponse(["@app_caching_post", "@app_caching_latest_posts"])]
    public function indexAction(Post $post): Response
    {
        // your code
        // won't be called in case of a 304
    }
}

当 Symfony 的路由选择此控制器动作时,所有 LastModifiedDeterminator 都会被调用以返回它们各自的上次修改日期。

在这种情况下,两个 LastModifiedDeterminators 都配置为服务:@app_caching_post@app_caching_latest_posts。第一个返回请求 $post 的更新日期,第二个可能使用从 DI 容器注入的 PostRepository 返回最后更新日期的 x 条最新帖子。

#[ReplaceWithNotModifiedResponse] 将所有 LastModifiedDeterminators 的日期组合起来,以确定整个页面的最后修改日期。最后,如果请求包含适当的 if-not-modified-since 头,则跳过控制器动作的执行,并返回一个带有 "304 Not Modified" 状态码的空响应。如果您的 LastModifiedDeterminators 很快,这可以大大提高您的性能。

我们喜欢 LastModifiedDeterminators 的地方在于,它们鼓励将关注点分离得很好,并将任务封装成易于理解、可重用和单元测试的小单元。

注意: #[ReplaceWithNotModifiedResponse] 不会更改或添加 Cache-Control 头设置。因此,默认情况下,您的响应将保持 private,并且仅在浏览器缓存中结束。如果您希望它被保留在代理缓存(如 Varnish 或 Symfony Http Cache)中,您可以添加 #[Cache(smaxage: 0)]。这将使响应变为 public,但也需要在每次请求时进行重新验证,因为响应总是被认为是过时的。有关 Symfony 的 HTTP 缓存的更多信息,请参阅 Symonfy 的 HTTP 缓存

用法

选择一个您可能希望替换为 304 Not Modified 响应的控制器动作。为不同的底层资源编写一个 LastModifiedDeterminator,实现 Webfactory\HttpCacheBundle\NotModified\LastModifiedDeterminator 接口。

<?php
// src/Caching/PostsLastModifiedDeterminator.php
namespace App\Caching;

use App\Entity\PostRepository;
use Symfony\Component\HttpFoundation\Request;
use Webfactory\HttpCacheBundle\NotModified\LastModifiedDeterminator;

/**
 * Returns the publishing date of the latest posts.
 */
final class PostsLastModifiedDeterminator implements LastModifiedDeterminator
{
    public function __construct(
        private readonly BlogPostRepository $blogPostRepository,
    ) {
    
    public function getLastModified(Request $request): ?\DateTime
    {
        $post = $this->blogPostRepository->findLatest();
        
        return $post?->getPublishingDate();
    }
}

您可以在 getLastModified 中使用 $request,例如获取路由参数,这在您在请求的 URL 中编写一些过滤器时是必要的。

如果您的 LastModifiedDeterminator 有依赖项,您希望注入,则将其配置为服务。

然后,将 #[ReplaceWithNotModifiedResponse] 属性添加到所选控制器方法中,并使用您的 LastModifiedDeterminators 参数化它

<?php

namespace src\Controller;

use Symfony\Component\HttpFoundation\Response;
use Webfactory\HttpCacheBundle\NotModified\Attribute\ReplaceWithNotModifiedResponse;

final class MyController
{
     #[ReplaceWithNotModifiedResponse([...])]
    public function indexAction()
    {
        // ...
        return new Response(...);
    }
}

添加 LastModifiedDeterminator 的最简单形式是传递其完全限定的类名

#[ReplaceWithNotModifiedResponse([\App\Caching\MySimpleLastModifiedDeterminator::class])]

如果您的 LastModifiedDeterminator 需要简单的构造函数参数,您可以将其作为数组传递

#[ReplaceWithNotModifiedResponse([\App\Caching\MyLastModifiedDeterminator::class => ["key1" => 1, "key2" => ["*"]]])]

这将传递数组 ['key1' => 1, 'key2' => ['*']] 作为 MyLastModifiedDeterminator 的构造函数参数。

如果您的 LastModifiedDeterminator 有更复杂的依赖项,您可以将其定义为服务,例如。

yaml // services.yml services: app_caching_latest_posts: class: App\Caching\PostsLastModifiedDeterminator arguments: - @repository_post

并将服务名称放入属性中

#[ReplaceWithNotModifiedResponse(["@app_caching_latest_posts"])]

要组合多个LastModifiedDeterminators,只需将它们全部添加到属性中

#[ReplaceWithNotModifiedResponse([
    "@app_caching_latest_posts",
    \App\Caching\MySimpleLastModifiedDeterminator::class,
    [\App\Caching\MyLastModifiedDeterminator::class => ["key1" = 1, "key2" => ["*"]]
])]

最新的最后修改日期决定了响应的最后修改日期。

版权信息、著作权和许可证

本捆绑包由德国波恩的webfactory GmbH启动。

版权所有 2018-2024 德国波恩webfactory GmbH。代码在MIT许可证下发布。