Symfony2 内核异常事件在生产模式下未处理致命错误异常



我做了一个侦听器来处理异常。下面是我的代码

services.yml

kernel.listener.prod_exception_listener:
    class: MyBundleListenerExceptionListener
    tags:
        - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

ExceptionListener.php

<?php
namespace MyBundleListener;
use SymfonyComponentHttpKernelEventGetResponseForExceptionEvent;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentHttpKernelExceptionHttpExceptionInterface;
class ExceptionListener
{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        // no fatal exception goes here others are coming in this function
        // like 403,404,500 are coming in this block
    }
}

对于生产模式下的致命异常,我需要做哪些额外的工作?因为在开发模式下,侦听器中会出现致命错误。

我按以下方式解决了它,在我的服务中。

api_exception_subscriber:
    class: AppBundleEventListenerApiExceptionSubscriber
    arguments: ['%kernel.debug%', '@api.response_factory', '@logger']
    tags:
        - { name: kernel.event_subscriber }
api.response_factory:
    class: AppBundleApiResponseFactory

我的响应工厂如下所示:

<?php
namespace AppBundleApi;
use SymfonyComponentHttpFoundationJsonResponse;
class ResponseFactory
{
    public function createResponse(ApiProblem $apiProblem)
    {
        $data = $apiProblem->toArray();
        $response = new JsonResponse(
            $data,
            $apiProblem->getStatusCode()
        );
        $response->headers->set('Content-Type', 'application/json');
        return $response;
    }
} 

和 API 订阅者类

<?php
namespace AppBundleEventListener;
use AppBundleApiApiProblem;
use AppBundleApiApiProblemException;
use AppBundleApiResponseFactory;
use PsrLogLoggerInterface;
use SymfonyComponentEventDispatcherEventSubscriberInterface;
use SymfonyComponentHttpKernelEventGetResponseForExceptionEvent;
use SymfonyComponentHttpKernelKernelEvents;
use SymfonyComponentHttpFoundationJsonResponse;
use SymfonyComponentHttpKernelExceptionHttpExceptionInterface;
class ApiExceptionSubscriber implements EventSubscriberInterface
{
    private $debug;
    private $responseFactory;
    private $logger;
    public function __construct($debug, ResponseFactory $responseFactory, LoggerInterface $logger)
    {
        $this->debug = $debug;
        $this->responseFactory = $responseFactory;
        $this->logger = $logger;
    }
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        // only reply to /api URLs
        if (strpos($event->getRequest()->getPathInfo(), '/api') !== 0) {
            return;
        }
        $e = $event->getException();
        $statusCode = $e instanceof HttpExceptionInterface ? $e->getStatusCode() : 500;
        // allow 500 errors to be thrown
        if ($this->debug && $statusCode >= 500) {
            return;
        }
        $this->logException($e);
        if ($e instanceof ApiProblemException) {
            $apiProblem = $e->getApiProblem();
        } else {

            $apiProblem = new ApiProblem(
                $statusCode
            );
            /*
             * If it's an HttpException message (e.g. for 404, 403),
             * we'll say as a rule that the exception message is safe
             * for the client. Otherwise, it could be some sensitive
             * low-level exception, which should *not* be exposed
             */
            if ($e instanceof HttpExceptionInterface) {
                $apiProblem->set('detail', $e->getMessage());
            }
        }
        $response = $this->responseFactory->createResponse($apiProblem);
        $event->setResponse($response);
    }
    public static function getSubscribedEvents()
    {
        return array(
            KernelEvents::EXCEPTION => 'onKernelException'
        );
    }
    /**
     * Adapted from the core Symfony exception handling in ExceptionListener
     *
     * @param Exception $exception
     */
    private function logException(Exception $exception)
    {
        $message = sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine());
        $isCritical = !$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500;
        $context = array('exception' => $exception);
        if ($isCritical) {
            $this->logger->critical($message, $context);
        } else {
            $this->logger->error($message, $context);
        }
    }
}

编辑2020:从Symfony 5开始,这不再是必需

我已经通过覆盖内核::句柄来手动调用异常侦听器来处理这个问题

public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true): Response
{
    try {
        return parent::handle($request, $type, $catch);
    } catch (Exception $exception) {
        throw new Exception("There was an issue booting the framework");
    } catch (Throwable $throwable) {
        $exception = new FatalThrowableError($throwable);
        $event = new ExceptionEvent($this, $request, $type, $exception);
        /** @var ExceptionListener $exceptionListener */
        $exceptionListener = $this->container->get(ExceptionListener::class);
        $exceptionListener->onKernelException($event);
        return $event->getResponse();
    }
}

相关内容

  • 没有找到相关文章

最新更新