Symfony-公共和管理部分的不同错误页面


Symfony - Different error pages for public and admin sections

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

这很好,但如果用户在网站的web或管理部分,我希望有不同的页面取消挂起。

有简单的方法吗?谢谢你,迈克。

我认为最好的方法是覆盖默认的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/views/Exceptions/
    • 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);
    }
}

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

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

对于Symfony 5,我就是这么做的,我相信它在Symfony 6中也会起作用。它不是很光滑,可以改进。

我在我的应用程序src'CustomerErrorRenderer.php中复制了vendor/symfony/twig-bridge/ErrorRenderer/TwigErrorRenderer.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