我做了一个侦听器来处理异常。下面是我的代码
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();
}
}