问题是,我在项目中错误配置了ExceptionController:
- FOSRestBundle 有自己的 ExceptionController 用于显示 json/xml 异常
您在 config/fos_rest.yaml 中配置它:
fos_rest
exception:
enabled: true
exception_controller: 'App\Controller\ExceptionController::handleExceptionAction'
- Twig 是 symfony 的默认异常控制器
您在 config/twig.yaml 中配置它:
twig:
debug: '%kernel.debug%'
exception_controller: App\Controller\ExceptionController::showException
使用的Exception Handler 是 fos_rest,它在我的 services.yaml 中我配置错误:
App\Controller\ExceptionController:
- '@fos_rest.view_handler.default'
- '@fos_rest.exception.messages_map'
- true <------ this should be '%kernel.debug%'
- '@templating.engine.twig'
- '@logger'
问题是,这里的第三个参数应该是 %kernel.debug%。
最后,我最终得到了 2 个异常控制器,一个用于具有 json 格式响应的 API,另一个用于 Sonata 后端,即标准的 Twig 异常控制器。唯一的解决方案是将请求从一个控制器转发到另一个:
<?php
namespace App\Controller;
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController as TwigExceptionController;
class ExceptionController
{
protected $debug;
/**
* @var TwigExceptionController
*/
protected $twigExceptionController;
/**
* @param bool $debug
* @param TwigExceptionController $twigExceptionController
*/
public function __construct(
bool $debug,
TwigExceptionController $twigExceptionController
)
{
$this->debug = $debug;
$this->twigExceptionController = $twigExceptionController;
}
/**
* Converts an Exception to a Response.
*
* @param Request $request
* @param FlattenException $exception
* @param LoggerInterface $logger
*
* @return Response
*/
public function handleExceptionAction(Request $request, FlattenException $exception, LoggerInterface $logger): Response
{
if (!stristr( strtolower($request->getUri()), '/api/')) {
return $this->twigExceptionController->showAction($request, $exception, $logger);
}
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
if ($currentContent) {
$logger->error('current content: '.$currentContent);
}
$code = $this->getStatusCode($exception);
$response = new JsonResponse();
$response->setStatusCode($code);
$response->setData([
'message' => $exception->getMessage(),
'code' => $code
]);
return $response;
}
/**
* Determines the status code to use for the response.
*
* @param \Exception $exception
*
* @return int
*/
protected function getStatusCode($exception): int
{
if (method_exists($exception, 'getCode') && $exception->getCode() > 0) {
return $exception->getCode();
}
if (method_exists($exception, 'getStatusCode') && $exception->getStatusCode() > 0) {
return $exception->getStatusCode();
}
return 500;
}
/**
* Gets and cleans any content that was already outputted.
*
* This code comes from Symfony and should be synchronized on a regular basis
* see src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php
*
* @return string
*/
private function getAndCleanOutputBuffering($startObLevel)
{
if (ob_get_level() <= $startObLevel) {
return '';
}
Response::closeOutputBuffers($startObLevel + 1, true);
return ob_get_clean();
}
}