Browse Source

Map non-http exceptions to their http codes in ExceptionRenderer

Corey Taylor 5 years ago
parent
commit
d2bce1d0cf

+ 2 - 6
src/Core/Exception/Exception.php

@@ -61,20 +61,16 @@ class Exception extends RuntimeException
      *
      * @param string|array $message Either the string of the error message, or an array of attributes
      *   that are made available in the view, and sprintf()'d into Exception::$_messageTemplate
-     * @param int|null $code The code of the error, is also the HTTP status code for the error.
+     * @param int|null $code The error code
      * @param \Throwable|null $previous the previous exception.
      */
     public function __construct($message = '', ?int $code = null, ?Throwable $previous = null)
     {
-        if ($code === null) {
-            $code = $this->_defaultCode;
-        }
-
         if (is_array($message)) {
             $this->_attributes = $message;
             $message = vsprintf($this->_messageTemplate, $message);
         }
-        parent::__construct($message, $code, $previous);
+        parent::__construct($message, $code ?? $this->_defaultCode, $previous);
     }
 
     /**

+ 23 - 9
src/Error/ExceptionRenderer.php

@@ -18,12 +18,17 @@ namespace Cake\Error;
 
 use Cake\Controller\Controller;
 use Cake\Controller\ControllerFactory;
+use Cake\Controller\Exception\MissingActionException;
 use Cake\Core\App;
 use Cake\Core\Configure;
 use Cake\Core\Exception\Exception as CakeException;
 use Cake\Core\Exception\MissingPluginException;
+use Cake\Datasource\Exception\InvalidPrimaryKeyException;
+use Cake\Datasource\Exception\PageOutOfBoundsException;
+use Cake\Datasource\Exception\RecordNotFoundException;
 use Cake\Event\Event;
 use Cake\Http\Exception\HttpException;
+use Cake\Http\Exception\MissingControllerException;
 use Cake\Http\Response;
 use Cake\Http\ServerRequest;
 use Cake\Http\ServerRequestFactory;
@@ -90,6 +95,17 @@ class ExceptionRenderer implements ExceptionRendererInterface
      */
     protected $request;
 
+    protected $exceptionHttpCodes = [
+        // Controller exceptions
+        MissingActionException::class => 404,
+        // Datasource exceptions
+        InvalidPrimaryKeyException::class => 404,
+        PageOutOfBoundsException::class => 404,
+        RecordNotFoundException::class => 404,
+        // Http exceptions
+        MissingControllerException::class => 404,
+    ];
+
     /**
      * Creates the controller to perform rendering on the error response.
      * If the error is a Cake\Core\Exception\Exception it will be converted to either a 400 or a 500
@@ -192,7 +208,7 @@ class ExceptionRenderer implements ExceptionRendererInterface
     public function render(): ResponseInterface
     {
         $exception = $this->error;
-        $code = $this->_code($exception);
+        $code = $this->getHttpCode($exception);
         $method = $this->_method($exception);
         $template = $this->_template($exception, $method, $code);
         $this->clearOutput();
@@ -341,20 +357,18 @@ class ExceptionRenderer implements ExceptionRendererInterface
     }
 
     /**
-     * Get HTTP status code.
+     * Gets the appropriate http status code for exception.
      *
      * @param \Throwable $exception Exception.
-     * @return int A valid HTTP error status code.
+     * @return int A valid HTTP status code.
      */
-    protected function _code(Throwable $exception): int
+    protected function getHttpCode(Throwable $exception): int
     {
-        $code = 500;
-        $errorCode = (int)$exception->getCode();
-        if ($errorCode >= 400 && $errorCode < 600) {
-            $code = $errorCode;
+        if ($exception instanceof HttpException) {
+            return (int)$exception->getCode();
         }
 
-        return $code;
+        return $this->exceptionHttpCodes[get_class($exception)] ?? 500;
     }
 
     /**

+ 0 - 5
src/Mailer/Exception/MissingActionException.php

@@ -25,9 +25,4 @@ class MissingActionException extends Exception
      * @inheritDoc
      */
     protected $_messageTemplate = 'Mail %s::%s() could not be found, or is not accessible.';
-
-    /**
-     * @inheritDoc
-     */
-    protected $_defaultCode = 404;
 }

+ 2 - 2
tests/TestCase/Error/ExceptionRendererTest.php

@@ -320,7 +320,7 @@ class ExceptionRendererTest extends TestCase
      */
     public function testUnknownExceptionTypeWithCodeHigherThan500()
     {
-        $exception = new \OutOfBoundsException('foul ball.', 501);
+        $exception = new HttpException('foul ball.', 501);
         $ExceptionRenderer = new ExceptionRenderer($exception);
         $response = $ExceptionRenderer->render();
         $result = (string)$response->getBody();
@@ -611,7 +611,7 @@ class ExceptionRendererTest extends TestCase
                     '/Missing Method in UserMailer/',
                     '/<em>UserMailer::welcome\(\)<\/em>/',
                 ],
-                404,
+                500,
             ],
             [
                 new Exception('boom'),

+ 1 - 1
tests/TestCase/ExceptionsTest.php

@@ -172,7 +172,7 @@ class ExceptionsTest extends TestCase
             ['Cake\Datasource\Exception\MissingModelException', 500],
             ['Cake\Datasource\Exception\PageOutOfBoundsException', 404],
             ['Cake\Datasource\Exception\RecordNotFoundException', 404],
-            ['Cake\Mailer\Exception\MissingActionException', 404],
+            ['Cake\Mailer\Exception\MissingActionException', 500],
             ['Cake\Mailer\Exception\MissingMailerException', 500],
             ['Cake\Http\Exception\BadRequestException', 400],
             ['Cake\Http\Exception\ConflictException', 409],