【问题标题】:Symfony - Different error pages for public and admin sectionsSymfony - 公共和管理部分的不同错误页面
【发布时间】:2016-04-10 00:10:50
【问题描述】:

我一直在遵循http://symfony.com/doc/current/cookbook/controller/error_pages.html 上的提示,并在 Resources/TwigBundle/views/Exceptions 中创建了新模板 error500.html.twig。

这很好用,但如果用户位于网站的网络或管理部分,我希望有不同的页面。

有没有简单的方法可以做到这一点? 谢谢你,迈克。

【问题讨论】:

    标签: php symfony


    【解决方案1】:

    我认为最好的方法是overriding the default ExceptionController。只需扩展它,并覆盖 findTemplate 方法。从请求的属性中检查是否设置了_route_controller,并对其进行处理。

    namespace AppBundle\Controller;
    
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Bundle\TwigBundle\Controller\ExceptionController as BaseExceptionController;
    
    class ExceptionController extends BaseExceptionController
    {
        protected function findTemplate(Request $request, $format, $code, $showException)
        {
            $routeName = $request->attributes->get('_route');
    
            // You can inject these routes in the construct of the controller
            // so that you can manage them from the configuration file instead of hardcode them here
            $routesAdminSection = ['admin', 'admin_ban', 'admin_list'];
    
            // This is a poor implementation with in_array.
            // You can implement more advanced options using regex 
            // so that if you pass "^admin" you can match all the routes that starts with admin.
    
            // If the route name match, then we want use a different template: admin_error_CODE.FORMAT.twig
            // example: admin_error_404.html.twig
            if (!$showException && in_array($routeName, $routesAdminSection, true)) {
                $template = sprintf('@AppBundle/Exception/admin_error_%s.%s.twig', $code, format);
                if ($this->templateExists($template)) {
                    return $template;
                }
    
                // What you want to do if the template doesn't exist?
                // Just use a generic HTML template: admin_error.html.twig
                $request->setRequestFormat('html');
                return sprintf('@AppBundle/Exception/admin_error.html.twig');
            }
    
            // Use the default findTemplate method
            return parent::findTemplate($request, $format, $code, $showException);
        }
    }
    

    然后配置twig.exception_controller:

    # app/config/services.yml
    services:
        app.exception_controller:
            class: AppBundle\Controller\ExceptionController
            arguments: ['@twig', '%kernel.debug%']
    

    # app/config/config.yml
    twig:
        exception_controller:  app.exception_controller:showAction
    

    然后您可以以相同的方式覆盖模板:

    • 资源/AppBundle/视图/异常/
      • admin_error.html.twig
      • admin_error_404.html.twig
      • admin_error_500.html.twig
      • ...

    更新

    执行此操作的更简单方法是在您的路线的defaults 集合中指定网站部分。示例:

    # app/config/routing.yml
    home:
        path:      /
        defaults:
            _controller: AppBundle:Main:index
            section:     web
    blog:
        path:      /blog/{page}
        defaults:
            _controller: AppBundle:Main:blog
            section:     web
    dashboard:
        path:      /admin
        defaults:
            _controller: AppBundle:Admin:dashboard
            section:     admin
    stats:
        path:      /admin/stats
        defaults:
            _controller: AppBundle:Admin:stats
            section:     admin
    

    然后你的控制器变成这样:

    namespace AppBundle\Controller;
    
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Bundle\TwigBundle\Controller\ExceptionController as BaseExceptionController;
    
    class ExceptionController extends BaseExceptionController
    {
        protected function findTemplate(Request $request, $format, $code, $showException)
        {
            $section = $request->attributes->get('section');
            $template = sprintf('@AppBundle/Exception/%s_error_%s.%s.twig', $section, $code, format);
            if ($this->templateExists($template)) {
                return $template;
            }
    
            return parent::findTemplate($request, $format, $code, $showException);
        }
    }
    

    并以与上述相同的方式配置twig.exception_controller。 现在您只需要为每个部分、代码和格式定义一个模板。

    • web_error_404.html.twig
    • web_error_500.html.twig
    • admin_error_404.html.twig
    • admin_error_500.html.twig
    • 等等……

    【讨论】:

    • 2022 年使用 Symfony 6 仍然是最好的方法吗?
    【解决方案2】:

    对于 Symfony 5,这就是我所做的,我相信它也适用于 Symfony 6。它不是很精致,可以改进。

    我将vendor/symfony/twig-bridge/ErrorRenderer/TwigErrorRenderer.php 复制到我的应用程序src\CustomerErrorRenderer.php

    有以下区别:

    public function __construct(
        Environment       $twig,
        RequestStack      $requestStack,
        HtmlErrorRenderer $fallbackErrorRenderer = null,
                          $debug = false
    ) {
        if (!\is_bool($debug) && !\is_callable($debug)) {
            throw new \TypeError(
                sprintf(
                    'Argument debug passed to "%s()" must be a boolean or a callable, "%s" given.',
                    __METHOD__,
                    get_debug_type($debug)
                )
            );
        }
    
        $this->twig = $twig;
        $this->fallbackErrorRenderer = $fallbackErrorRenderer ?? new HtmlErrorRenderer();
        $this->debug = $debug;
        $this->requestStack = $requestStack;
    }
    
    
    private function findTemplate(int $statusCode, RequestStack $requestStack): ?string
    {
        $requestUri = $requestStack->getCurrentRequest()
                                   ->getRequestUri();
    
        $prefix = '';
        if ($this->startsWith($requestUri, '/admin/')) {
            $prefix = 'admin/';
        }
        $template = sprintf('@Twig/Exception/%serror%s.html.twig', $prefix, $statusCode);
        if (
            $this->twig->getLoader()
                       ->exists($template)
        ) {
            return $template;
        }
    
        $template = sprintf('@Twig/Exception/%serror.html.twig', $prefix);
        if (
            $this->twig->getLoader()
                       ->exists($template)
        ) {
            return $template;
        }
    
        return null;
    }
    
    
    private function startsWith(string $string, string $startString): bool
    {
        $len = strlen($startString);
        return (substr($string, 0, $len) === $startString);
    }
    

    然后覆盖services.yaml中的error_renderer:

    error_renderer:
      class: App\Twig\CustomErrorRenderer
      arguments:
        - '@twig'
        - '@request_stack'
        - '@twig.error_renderer.html.inner'
        - !service
          factory: [ 'Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer', 'getAndCleanOutputBuffer' ]
          arguments: ['@request_stack']
    

    在 routes/framework.yaml、/admin/_error/404、/admin/_error/500 中测试(复制来自 resource: '@FrameworkBundle/Resources/config/routing/errors.xml' 的参数)

    when@dev:
    _errors:
        path: /_error/{code}.{_format}
        controller: 'error_controller::preview'
        defaults:
            _format: 'html'
        requirements:
            code: '\d+'
    
    _errors_admin:
        path: /admin/_error/{code}.{_format}
        controller: 'error_controller::preview'
        defaults:
            _format: 'html'
        requirements:
            code: '\d+'
    

    还有 config/packages/security.yaml

    security:
        firewalls:
            dev:
                pattern: ^/(_(profiler|wdt)|css|images|js|admin\/_error)/
            security: false
    

    中创建的模板
    templates/bundles/TwigBundle/Exception/admin/
    templates/bundles/TwigBundle/Exception/admin/error.html.twig 
    templates/bundles/TwigBundle/Exception/admin/error404.html.twig 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-27
      • 1970-01-01
      • 1970-01-01
      • 2011-07-24
      • 2014-03-22
      相关资源
      最近更新 更多