Skip to content

Commit 05330ba

Browse files
committed
Refactor the Exception controller to remove the ExceptionWrapper
1 parent 6744a48 commit 05330ba

37 files changed

+680
-600
lines changed

Controller/ExceptionController.php

Lines changed: 53 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -11,241 +11,97 @@
1111

1212
namespace FOS\RestBundle\Controller;
1313

14-
use FOS\RestBundle\Negotiation\FormatNegotiator;
15-
use FOS\RestBundle\Util\StopFormatListenerException;
16-
use FOS\RestBundle\Util\ExceptionWrapper;
17-
use FOS\RestBundle\View\ExceptionWrapperHandlerInterface;
14+
use FOS\RestBundle\Util\ClassMapHandlerTrait;
1815
use FOS\RestBundle\View\View;
1916
use FOS\RestBundle\View\ViewHandlerInterface;
2017
use Symfony\Component\HttpFoundation\Request;
2118
use Symfony\Component\HttpFoundation\Response;
2219
use Symfony\Component\Debug\Exception\FlattenException;
20+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
2321
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
2422

2523
/**
2624
* Custom ExceptionController that uses the view layer and supports HTTP response status code mapping.
2725
*/
2826
class ExceptionController
2927
{
30-
private $exceptionWrapperHandler;
31-
private $formatNegotiator;
28+
use ClassMapHandlerTrait;
29+
3230
private $viewHandler;
3331
private $exceptionCodes;
34-
private $exceptionMessages;
3532
private $showException;
3633

3734
public function __construct(
38-
ExceptionWrapperHandlerInterface $exceptionWrapperHandler,
39-
FormatNegotiator $formatNegotiator,
4035
ViewHandlerInterface $viewHandler,
4136
array $exceptionCodes,
42-
array $exceptionMessages,
4337
$showException
4438
) {
45-
$this->exceptionWrapperHandler = $exceptionWrapperHandler;
46-
$this->formatNegotiator = $formatNegotiator;
4739
$this->viewHandler = $viewHandler;
4840
$this->exceptionCodes = $exceptionCodes;
49-
$this->exceptionMessages = $exceptionMessages;
5041
$this->showException = $showException;
5142
}
5243

53-
/**
54-
* @return ViewHandlerInterface
55-
*/
56-
protected function getViewHandler()
57-
{
58-
return $this->viewHandler;
59-
}
60-
6144
/**
6245
* Converts an Exception to a Response.
6346
*
64-
* @param Request $request
65-
* @param FlattenException $exception
66-
* @param DebugLoggerInterface $logger
47+
* @param Request $request
48+
* @param \Exception $exception
49+
* @param DebugLoggerInterface|null $logger
6750
*
6851
* @throws \InvalidArgumentException
6952
*
7053
* @return Response
7154
*/
72-
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
55+
public function showAction(Request $request, \Exception $exception, DebugLoggerInterface $logger = null)
7356
{
74-
try {
75-
$format = $this->getFormat($request, $request->getRequestFormat());
76-
} catch (\Exception $e) {
77-
$format = null;
78-
}
79-
if (null === $format) {
80-
$message = 'No matching accepted Response format could be determined, while handling: ';
81-
$message .= $this->getExceptionMessage($exception);
82-
83-
return $this->createPlainResponse($message, Response::HTTP_NOT_ACCEPTABLE, $exception->getHeaders());
84-
}
85-
86-
$currentContent = $this->getAndCleanOutputBuffering(
87-
$request->headers->get('X-Php-Ob-Level', -1)
88-
);
57+
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
8958
$code = $this->getStatusCode($exception);
90-
$parameters = $this->getParameters($currentContent, $code, $exception, $logger, $format);
91-
$showException = $request->attributes->get('showException', $this->showException);
92-
93-
try {
94-
$view = $this->createView($format, $exception, $code, $parameters, $request, $showException);
59+
$templateData = $this->getTemplateData($currentContent, $code, $exception, $logger);
9560

96-
$response = $this->viewHandler->handle($view);
97-
} catch (\Exception $e) {
98-
$message = 'An Exception was thrown while handling: ';
99-
$message .= $this->getExceptionMessage($exception);
100-
$response = $this->createPlainResponse($message, Response::HTTP_INTERNAL_SERVER_ERROR, $exception->getHeaders());
101-
}
61+
$view = $this->createView($exception, $code, $templateData, $request, $this->showException);
62+
$response = $this->viewHandler->handle($view);
10263

10364
return $response;
10465
}
10566

10667
/**
107-
* Returns a Response Object with content type text/plain.
108-
*
109-
* @param string $content
110-
* @param int $status
111-
* @param array $headers
112-
*
113-
* @return Response
114-
*/
115-
private function createPlainResponse($content, $status, $headers)
116-
{
117-
$headers['content-type'] = 'text/plain';
118-
119-
return new Response($content, $status, $headers);
120-
}
121-
122-
/**
123-
* Creates a new ExceptionWrapper instance that can be overwritten by a custom
124-
* ExceptionController class.
125-
*
126-
* @param array $parameters output data
127-
*
128-
* @return ExceptionWrapper ExceptionWrapper instance
129-
*/
130-
protected function createExceptionWrapper(array $parameters)
131-
{
132-
return $this->exceptionWrapperHandler->wrap($parameters);
133-
}
134-
135-
/**
136-
* @param string $format
137-
* @param FlattenException $exception
138-
* @param int $code
139-
* @param array $parameters
140-
* @param Request $request
141-
* @param bool $showException
68+
* @param \Exception $exception
69+
* @param int $code
70+
* @param array $templateData
71+
* @param Request $request
72+
* @param bool $showException
14273
*
14374
* @return View
14475
*/
145-
protected function createView($format, FlattenException $exception, $code, $parameters, Request $request, $showException)
76+
protected function createView(\Exception $exception, $code, array $templateData, Request $request, $showException)
14677
{
147-
$parameters = $this->createExceptionWrapper($parameters);
148-
$view = View::create($parameters, $code, $exception->getHeaders());
149-
$view->setFormat($format);
78+
$view = new View($exception, $code, $exception instanceof HttpExceptionInterface ? $exception->getHeaders() : []);
79+
$view->setTemplateVar('raw_exception');
80+
$view->setTemplateData($templateData);
15081

15182
return $view;
15283
}
15384

154-
/**
155-
* Gets and cleans any content that was already outputted.
156-
*
157-
* This code comes from Symfony and should be synchronized on a regular basis
158-
* see src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php
159-
*
160-
* @return string
161-
*/
162-
private function getAndCleanOutputBuffering($startObLevel)
163-
{
164-
if (ob_get_level() <= $startObLevel) {
165-
return '';
166-
}
167-
Response::closeOutputBuffers($startObLevel + 1, true);
168-
169-
return ob_get_clean();
170-
}
171-
172-
/**
173-
* Extracts the exception message.
174-
*
175-
* @param FlattenException $exception
176-
* @param array $exceptionMap
177-
*
178-
* @return int|false
179-
*/
180-
private function isSubclassOf($exception, $exceptionMap)
181-
{
182-
$exceptionClass = $exception->getClass();
183-
foreach ($exceptionMap as $exceptionMapClass => $value) {
184-
if ($value
185-
&& ($exceptionClass === $exceptionMapClass || is_subclass_of($exceptionClass, $exceptionMapClass))
186-
) {
187-
return $value;
188-
}
189-
}
190-
191-
return false;
192-
}
193-
194-
/**
195-
* Extracts the exception message.
196-
*
197-
* @param FlattenException $exception
198-
*
199-
* @return string Message
200-
*/
201-
protected function getExceptionMessage($exception)
202-
{
203-
$showExceptionMessage = $this->isSubclassOf($exception, $this->exceptionMessages);
204-
205-
if ($showExceptionMessage || $this->showException) {
206-
return $exception->getMessage();
207-
}
208-
209-
$statusCode = $this->getStatusCode($exception);
210-
211-
return array_key_exists($statusCode, Response::$statusTexts) ? Response::$statusTexts[$statusCode] : 'error';
212-
}
213-
21485
/**
21586
* Determines the status code to use for the response.
21687
*
217-
* @param FlattenException $exception
88+
* @param \Exception $exception
21889
*
21990
* @return int
22091
*/
221-
protected function getStatusCode($exception)
92+
protected function getStatusCode(\Exception $exception)
22293
{
223-
$isExceptionMappedToStatusCode = $this->isSubclassOf($exception, $this->exceptionCodes);
224-
225-
return $isExceptionMappedToStatusCode ?: $exception->getStatusCode();
226-
}
94+
// If matched
95+
if ($statusCode = $this->resolveValue(get_class($exception), $this->exceptionCodes)) {
96+
return $statusCode;
97+
}
22798

228-
/**
229-
* Determines the format to use for the response.
230-
*
231-
* @param Request $request
232-
* @param string $format
233-
*
234-
* @return string
235-
*/
236-
protected function getFormat(Request $request, $format)
237-
{
238-
try {
239-
$accept = $this->formatNegotiator->getBest('', []);
240-
if ($accept) {
241-
$format = $request->getFormat($accept->getType());
242-
}
243-
$request->attributes->set('_format', $format);
244-
} catch (StopFormatListenerException $e) {
245-
$format = $request->getRequestFormat();
99+
// Otherwise, default
100+
if ($exception instanceof HttpExceptionInterface) {
101+
return $exception->getStatusCode();
246102
}
247103

248-
return $format;
104+
return 500;
249105
}
250106

251107
/**
@@ -256,21 +112,38 @@ protected function getFormat(Request $request, $format)
256112
*
257113
* @param string $currentContent
258114
* @param int $code
259-
* @param FlattenException $exception
115+
* @param \Exception $exception
260116
* @param DebugLoggerInterface $logger
261-
* @param string $format
262117
*
263118
* @return array
264119
*/
265-
protected function getParameters($currentContent, $code, $exception, DebugLoggerInterface $logger = null, $format = 'html')
120+
private function getTemplateData($currentContent, $code, \Exception $exception, DebugLoggerInterface $logger = null)
266121
{
267122
return [
123+
'exception' => FlattenException::create($exception),
268124
'status' => 'error',
269125
'status_code' => $code,
270126
'status_text' => array_key_exists($code, Response::$statusTexts) ? Response::$statusTexts[$code] : 'error',
271127
'currentContent' => $currentContent,
272-
'message' => $this->getExceptionMessage($exception),
273-
'exception' => $exception,
128+
'logger' => $logger,
274129
];
275130
}
131+
132+
/**
133+
* Gets and cleans any content that was already outputted.
134+
*
135+
* This code comes from Symfony and should be synchronized on a regular basis
136+
* see src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php
137+
*
138+
* @return string
139+
*/
140+
private function getAndCleanOutputBuffering($startObLevel)
141+
{
142+
if (ob_get_level() <= $startObLevel) {
143+
return '';
144+
}
145+
Response::closeOutputBuffers($startObLevel + 1, true);
146+
147+
return ob_get_clean();
148+
}
276149
}

Controller/TemplatingExceptionController.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
namespace FOS\RestBundle\Controller;
1313

14-
use FOS\RestBundle\Negotiation\FormatNegotiator;
15-
use FOS\RestBundle\View\ExceptionWrapperHandlerInterface;
1614
use FOS\RestBundle\View\ViewHandlerInterface;
1715
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
1816
use Symfony\Component\HttpFoundation\Request;
@@ -23,15 +21,12 @@ abstract class TemplatingExceptionController extends ExceptionController
2321
protected $templating;
2422

2523
public function __construct(
26-
ExceptionWrapperHandlerInterface $exceptionWrapperHandler,
27-
FormatNegotiator $formatNegotiator,
2824
ViewHandlerInterface $viewHandler,
2925
array $exceptionCodes,
30-
array $exceptionMessages,
3126
$showException,
3227
EngineInterface $templating
3328
) {
34-
parent::__construct($exceptionWrapperHandler, $formatNegotiator, $viewHandler, $exceptionCodes, $exceptionMessages, $showException);
29+
parent::__construct($viewHandler, $exceptionCodes, $showException);
3530

3631
$this->templating = $templating;
3732
}

Controller/TwigExceptionController.php

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
1515
use Symfony\Component\Debug\Exception\FlattenException;
1616
use Symfony\Component\HttpFoundation\Request;
17-
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
1817

1918
/**
2019
* Custom ExceptionController that uses the view layer and supports HTTP response status code mapping.
@@ -28,29 +27,11 @@ class TwigExceptionController extends TemplatingExceptionController
2827
protected function createView($format, FlattenException $exception, $code, $parameters, Request $request, $showException)
2928
{
3029
$view = parent::createView($format, $exception, $code, $parameters, $request, $showException);
31-
32-
if ($this->getViewHandler()->isFormatTemplating($format)) {
33-
$view->setTemplate($this->findTemplate($request, $format, $code, $showException));
34-
$view->setData($parameters);
35-
}
30+
$view->setTemplate($this->findTemplate($request, $format, $code, $showException));
3631

3732
return $view;
3833
}
3934

40-
/**
41-
* {@inheritdoc}
42-
*/
43-
protected function getParameters($currentContent, $code, $exception, DebugLoggerInterface $logger = null, $format = 'html')
44-
{
45-
$parameters = parent::getParameters($currentContent, $code, $exception, $logger, $format);
46-
47-
if ($this->getViewHandler()->isFormatTemplating($format)) {
48-
$parameters['logger'] = $logger;
49-
}
50-
51-
return $parameters;
52-
}
53-
5435
/**
5536
* {@inheritdoc}
5637
*

0 commit comments

Comments
 (0)