【问题标题】:ESI cache on specific route特定路由上的 ESI 缓存
【发布时间】:2016-12-01 16:33:31
【问题描述】:

如何让 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
        ]);
    }

菜单/main_menu_esi.html.twig

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

【问题讨论】:

    标签: symfony varnish esi


    【解决方案1】:

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

    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 类:

    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 }
    

    【讨论】:

      【解决方案2】:

      我对 Symfony 的了解不够,但总的来说,如果你能够从 cookie 中获取所需的状态,你可以试试这个场景:

      在recv()中:

      • 解析 Cookie 标头并将您的状态提取到自定义请求 HTTP 标头,比如“X-Menu-Mode: admin”
      • 删除所有 cookie

      在哈希()中:

      • 将该标头添加到哈希(AFAIK 默认有 URL 和主机)

      在未命中/通过 (3.x) 或 backend_fetch (4.x) 中:

      • 从 X-Menu-Mode 重建 bereq 中的 Cookie 标头
      • 从 bereq 中删除 X-Menu-Mode

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

      【讨论】:

        【解决方案3】:

        你不能。

        对于 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}))  }}
        

        【讨论】:

        • 感谢您的回答。我需要对此进行确认。 :)
        猜你喜欢
        • 2016-09-14
        • 2022-11-25
        • 1970-01-01
        • 2012-07-11
        • 2023-04-03
        • 1970-01-01
        • 1970-01-01
        • 2020-11-08
        • 2016-06-07
        相关资源
        最近更新 更多