使用Symfony内置反向代理的FOSHttpCacheBundle缓存失效不起作用



我正在尝试做一件困难的事情:使用FOSHttpCacheBundle 2.9.0和内置的Symfony反向代理,用Symfony 4.4.13实现缓存无效。不幸的是,我不能使用其他缓存解决方案(如Varnish或Nginx(,因为我的托管服务不提供它们。因此,Symfony内置的反向代理是我唯一的解决方案。

我已经安装并配置了FOSHttpCacheBundle(遵循文档(。还创建了一个CacheKernel类并修改了Kernel以使用它(遵循Symfony官方文档、FOSHttpCache文档和FOSHttpCacheBundle文档(。

经过几次测试(使用我的浏览器(,HTTP缓存工作正常,GET响应被缓存(在浏览器网络分析器中可以看到(。但是,当我使用PUT/PATCH/POST更新资源时,GET响应仍然来自缓存,并且在到期之前保持不变。我的推论是无效。

我做错什么了吗?你能帮我排除故障吗?请参阅下面的代码和配置。

config/packages/fos_http_cache.yaml

fos_http_cache:
cache_control:
rules:
-
match:
path: ^/
headers:
cache_control:
public: true
max_age: 15
s_maxage: 30
etag: "strong"
cache_manager:
enabled: true
invalidation:
enabled: true
proxy_client:
symfony:
tags_header: My-Cache-Tags
tags_method: TAGPURGE
header_length: 1234
purge_method: PURGE
use_kernel_dispatcher: true

src/CacheKernel.php

<?php
namespace App;
use FOSHttpCacheSymfonyCacheCacheInvalidation;
use FOSHttpCacheSymfonyCacheCustomTtlListener;
use FOSHttpCacheSymfonyCacheDebugListener;
use FOSHttpCacheSymfonyCacheEventDispatchingHttpCache;
use FOSHttpCacheSymfonyCachePurgeListener;
use FOSHttpCacheSymfonyCacheRefreshListener;
use FOSHttpCacheSymfonyCacheUserContextListener;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpKernelHttpCacheHttpCache;
use SymfonyComponentHttpKernelHttpCacheStore;
use SymfonyComponentHttpKernelHttpKernelInterface;
class CacheKernel extends HttpCache implements CacheInvalidation
{
use EventDispatchingHttpCache;
// Overwrite constructor to register event listeners for FOSHttpCache.
public function __construct(HttpKernelInterface $kernel, SurrogateInterface $surrogate = null, array $options = [])
{
parent::__construct($kernel, new Store($kernel->getCacheDir()), $surrogate, $options);
$this->addSubscriber(new CustomTtlListener());
$this->addSubscriber(new PurgeListener());
$this->addSubscriber(new RefreshListener());
$this->addSubscriber(new UserContextListener());
if (isset($options['debug']) && $options['debug'])
$this->addSubscriber(new DebugListener());
}
// Made public to allow event listeners to do refresh operations.
public function fetch(Request $request, $catch = false)
{
return parent::fetch($request, $catch);
}
}

src/Kernel.php

<?php
namespace App;
use FOSHttpCacheSymfonyCacheHttpCacheAware;
use FOSHttpCacheSymfonyCacheHttpCacheProvider;
use SymfonyBundleFrameworkBundleKernelMicroKernelTrait;
use SymfonyComponentConfigLoaderLoaderInterface;
use SymfonyComponentConfigResourceFileResource;
use SymfonyComponentDependencyInjectionContainerBuilder;
use SymfonyComponentHttpKernelKernel as BaseKernel;
use SymfonyComponentRoutingRouteCollectionBuilder;
class Kernel extends BaseKernel implements HttpCacheProvider
{
use MicroKernelTrait;
use HttpCacheAware;
private const CONFIG_EXTS = '.{php,xml,yaml,yml}';
public function __construct(string $environment, bool $debug)
{
parent::__construct($environment, $debug);
$this->setHttpCache(new CacheKernel($this));
}
...

public/index.php

<?php
use AppKernel;
use SymfonyComponentErrorHandlerDebug;
use SymfonyComponentHttpFoundationRequest;
require dirname(__DIR__).'/config/bootstrap.php';
...
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$kernel = $kernel->getHttpCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

其中一个矿井控制器,src/controller/SectionController.php(注意:路线在YAML文件中定义(

<?php
namespace AppController;
use AppEntitySection;
use AppEntitySectionCollection;
use AppFormSectionType;
use FOSHttpCacheBundleConfigurationInvalidateRoute;
use FOSRestBundleControllerAbstractFOSRestController;
use FOSRestBundleControllerAnnotations as Rest;
use FOSRestBundleViewView;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentHttpKernelExceptionHttpException;
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;
class SectionController extends AbstractFOSRestController
{
/**
* List all sections.
*
* @RestView
* @param Request $request the request object
* @return array
*
* Route: get_sections
*/
public function getSectionsAction(Request $request)
{
return new SectionCollection($this->getDoctrine()->getRepository(Section::class)->findAll());
}
/**
* Get a single section.
*
* @RestView
* @param Request $request the request object
* @param int     $id      the section id
* @return array
* @throws NotFoundHttpException when section not exist
*
* Route: get_section
*/
public function getSectionAction(Request $request, $id)
{
if (!$section = $this->getDoctrine()->getRepository(Section::class)->find($id))
throw $this->createNotFoundException('Section does not exist.');
return array('section' => $section);
}
/**
* Get friends of the section's user.
*
* @RestView
* @return array
*
* Route: get_friendlysections
*/
public function getFriendlysectionsAction()
{
return $this->get('security.token_storage')->getToken()->getUser()->getSection()->getMyFriends();
}
private function processForm(Request $request, Section $section)
{
$em = $this->getDoctrine()->getManager();
$statusCode = $em->contains($section) ? Response::HTTP_NO_CONTENT : Response::HTTP_CREATED;
$form = $this->createForm(SectionType::class, $section, array('method' => $request->getMethod()));
// If PATCH method, don't clear missing data.
$form->submit($request->request->get($form->getName()), $request->getMethod() === 'PATCH' ? false : true);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($section);
$em->flush();
$response = new Response();
$response->setStatusCode($statusCode);
// set the 'Location' header only when creating new resources
if ($statusCode === Response::HTTP_CREATED) {
$response->headers->set('Location',
$this->generateUrl(
'get_section', array('id' => $section->getId()),
true // absolute
)
);
}
return $response;
}
return View::create($form, Response::HTTP_BAD_REQUEST);
}
/**
*
* Creates a new section from the submitted data.
*
* @RestView
* @return FormTypeInterface[]
*
* @InvalidateRoute("get_friendlysections")
* @InvalidateRoute("get_sections")
*
* Route: post_section
*/
public function postSectionsAction(Request $request)
{
return $this->processForm($request, new Section());
}
/**
* Update existing section from the submitted data.
*
* @RestView
* @param int     $id      the section id
* @return FormTypeInterface[]
* @throws NotFoundHttpException when section not exist
*
* @InvalidateRoute("get_friendlysections")
* @InvalidateRoute("get_sections")
* @InvalidateRoute("get_section", params={"id" = {"expression"="id"}})")
*
* Route: put_section
*/
public function putSectionsAction(Request $request, $id)
{
if (!$section = $this->getDoctrine()->getRepository(Section::class)->find($id))
throw $this->createNotFoundException('Section does not exist.');
return $this->processForm($request, $section);
}
/**
* Partially update existing section from the submitted data.
*
* @RestView
* @param int     $id      the section id
* @return FormTypeInterface[]
* @throws NotFoundHttpException when section not exist
*
* @InvalidateRoute("get_friendlysections")
* @InvalidateRoute("get_sections")
* @InvalidateRoute("get_section", params={"id" = {"expression"="id"}})")
*
* Route: patch_section
*/
public function patchSectionsAction(Request $request, $id)
{
return $this->putSectionsAction($request, $id);
}
/**
* Remove a section.
*
* @RestView(statusCode=204)
* @param int     $id      the section id
* @return View
*
* @InvalidateRoute("get_friendlysections")
* @InvalidateRoute("get_sections")
* @InvalidateRoute("get_section", params={"id" = {"expression"="id"}})")
*
* Route: delete_section
*/
public function deleteSectionsAction($id)
{
$em = $this->getDoctrine()->getManager();
if ($section = $this->getDoctrine()->getRepository(Section::class)->find($id)) {
$em->remove($section);
$em->flush();
}
}
}

经过几天的搜索,我自己找到了解决方案。

CacheKernel中,我按照FOSHttpCache文档中的描述扩展了SymfonyComponentHttpKernelHttpCacheHttpCache。但是,该类必须按照Symfony文档中的描述扩展SymfonyBundleFrameworkBundleHttpCacheHttpCache。结果,构造函数也发生了变化。

老实说,我不知道这两个类之间的区别,但如果你想有一个内置的函数反向代理,你必须使用第二个。它现在对我有效。

我把src/CacheKernel.php的最终代码放在这里:

<?php
namespace App;
use FOSHttpCacheSymfonyCacheCacheInvalidation;
use FOSHttpCacheSymfonyCacheCustomTtlListener;
use FOSHttpCacheSymfonyCacheDebugListener;
use FOSHttpCacheSymfonyCacheEventDispatchingHttpCache;
use FOSHttpCacheSymfonyCachePurgeListener;
use FOSHttpCacheSymfonyCacheRefreshListener;
use FOSHttpCacheSymfonyCacheUserContextListener;
use SymfonyBundleFrameworkBundleHttpCacheHttpCache;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpKernelHttpKernelInterface;
class CacheKernel extends HttpCache implements CacheInvalidation
{
use EventDispatchingHttpCache;
/**
* Overwrite constructor to register event listeners for FOSHttpCache.
*/
public function __construct(HttpKernelInterface $kernel)
{
parent::__construct($kernel, $kernel->getCacheDir());
$this->addSubscriber(new CustomTtlListener());
$this->addSubscriber(new PurgeListener());
$this->addSubscriber(new RefreshListener());
$this->addSubscriber(new UserContextListener());
if (isset($options['debug']) && $options['debug'])
$this->addSubscriber(new DebugListener());
}
/**
* Made public to allow event listeners to do refresh operations.
*
* {@inheritDoc}
*/
public function fetch(Request $request, $catch = false)
{
return parent::fetch($request, $catch);
}
}

代码的其余部分不会更改。

希望能有所帮助。再见

相关内容

  • 没有找到相关文章

最新更新