1111
1212namespace 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 ;
1815use FOS \RestBundle \View \View ;
1916use FOS \RestBundle \View \ViewHandlerInterface ;
2017use Symfony \Component \HttpFoundation \Request ;
2118use Symfony \Component \HttpFoundation \Response ;
2219use Symfony \Component \Debug \Exception \FlattenException ;
20+ use Symfony \Component \HttpKernel \Exception \HttpExceptionInterface ;
2321use Symfony \Component \HttpKernel \Log \DebugLoggerInterface ;
2422
2523/**
2624 * Custom ExceptionController that uses the view layer and supports HTTP response status code mapping.
2725 */
2826class 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}
0 commit comments