特定路由上的 ESI 缓存

ESI cache on specific route

我怎样才能让 varnish 缓存处于不同状态的动态菜单?

我当前的项目(基于 Symfony 2.8)使用 KnpMenuBundle 和 varnish 来缓存页面。它还使用 ESI 来禁用缓存某些页面上的特定元素。其中包括菜单。但是由于这不是一个变化很大的元素,我想知道是否可以缓存菜单的不同状态并将相关状态传递给当前调用菜单的页面。

涉及的主要文件如下:

main.html.twig

{{ render_esi(controller('AppBundle:Menu:mainESI')) }}

AppBundle\Controller\MenuController.php

    public function mainESIAction($path = null)
    {
        return $this->render('menu/main_menu_esi.html.twig', [
            'path' => $path
        ]);
    }

menu/main_menu_esi.html.twig

    {{ knp_menu_render('main-menu', {'template':'menu/main_menu.html.twig'}) }}

你不能。

对于 Varnish,url 将是相同的,因此将呈现相同的效果。

在外部应用您的逻辑并向路由添加参数:

{% if is_granted('ROLE_ADMIN') %}
    {% set menu_mode = 'admin' %}
{% else %}
    {% set menu_mode = 'normal' %}
{% endif %}

{{ render_ssi(controller('AppBundle:Menu:mainESI',{'menu_mode':menu_mode}))  }}

我对 Symfony 了解不够,但一般来说,如果您能够从 cookie 中获取所需的状态,您可以尝试这种情况:

在 recv() 中:

  • 解析 Cookie header 并将您的状态提取到自定义请求 HTTP header,比方说 "X-Menu-Mode: admin"
  • 删除所有 cookie

在哈希()中:

  • 将 header 添加到哈希(默认情况下有 URL 和 Host AFAIK)

在 miss/pass (3.x) 或 backend_fetch (4.x):

  • 从 X-Menu-Mode
  • 在 bereq 中重建 Cookie header
  • 从 bereq
  • 中删除 X-Menu-Mode

我已经成功地使用了这个场景。 ESI 包含的片段现在完全可缓存,每个唯一的 cookie 值都有不同的版本。

所以这毕竟是可能的。满足我需求的解决方案如下:

  1. 通过树枝传递当前路径:

AppBundle/Resources/views/base.html.twig

{{ render_esi(controller('AppBundle:Menu:mainESI', { 'currentPath': currentPath })) }}
  1. 获取控制器动作中的变量:

AppBundle/Controller/MenuAdminController.php

public function mainESIAction($currentPath = null) 
{
    return $this->render('menu/main_menu_esi.html.twig', [
         'currentPath' => $currentPath,
    ]);
}
  1. 使用 Matcher/Voter class:

AppBundle/Matcher/Voter/PathVoter.php

<?php

namespace AppBundle\Matcher\Voter;

use Knp\Menu\ItemInterface;
use Knp\Menu\Matcher\Voter\VoterInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Class PathVoter
 */
class PathVoter implements VoterInterface
{
    protected $uri;

    /**
     * @param RequestStack $requestStack
     */
    public function setRequest(RequestStack $requestStack)
    {
        if (($request = $requestStack->getCurrentRequest())) {
            $pathInfo = $request->getPathInfo();
            $requestUri = $request->getRequestUri();

            if (!empty($pathInfo) && '/_fragment' === $pathInfo && !empty($request->attributes->get('currentPath'))) {
                $this->uri = $request->attributes->get('currentPath');
            } else {
                $this->uri = $pathInfo;
            }
        }
    }

    /**
     * @param ItemInterface $item
     * @return bool|void
     */
    public function matchItem(ItemInterface $item)
    {
        $uri = $item->getUri();

        if ((null === $this->uri) || (null === $uri)) {
            return;
        }

        if ($item->getUri() === $this->uri) {
            return true;
        }

        return;
    }
}
  1. 选民登记

AppBundle/Resources/config/services.yml

parameters:
    app.menu.voter.class: AppBundle\MenuBundle\Matcher\Voter\PathVoter

services:
    app.menu.voter:
    class: %app.menu.voter.class%
    calls:
        - [setRequest, [@request_stack]]
    tags:
        - { name: knp_menu.voter }