diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index f2672cf57..36bdf845d 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -9,7 +9,7 @@ use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; -use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Throwable; class Handler extends ExceptionHandler @@ -82,7 +82,7 @@ class Handler extends ExceptionHandler $code = 500; $headers = []; - if ($e instanceof HttpException) { + if ($e instanceof HttpExceptionInterface) { $code = $e->getStatusCode(); $headers = $e->getHeaders(); } @@ -103,10 +103,6 @@ class Handler extends ExceptionHandler $code = $e->status; } - if (method_exists($e, 'getStatus')) { - $code = $e->getStatus(); - } - $responseData['error']['code'] = $code; return new JsonResponse($responseData, $code, $headers); diff --git a/app/Exceptions/NotifyException.php b/app/Exceptions/NotifyException.php index 307916db7..b62b8fde6 100644 --- a/app/Exceptions/NotifyException.php +++ b/app/Exceptions/NotifyException.php @@ -4,29 +4,39 @@ namespace BookStack\Exceptions; use Exception; use Illuminate\Contracts\Support\Responsable; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; -class NotifyException extends Exception implements Responsable +class NotifyException extends Exception implements Responsable, HttpExceptionInterface { public $message; - public $redirectLocation; - protected $status; + public string $redirectLocation; + protected int $status; public function __construct(string $message, string $redirectLocation = '/', int $status = 500) { $this->message = $message; $this->redirectLocation = $redirectLocation; $this->status = $status; + parent::__construct(); } /** - * Get the desired status code for this exception. + * Get the desired HTTP status code for this exception. */ - public function getStatus(): int + public function getStatusCode(): int { return $this->status; } + /** + * Get the desired HTTP headers for this exception. + */ + public function getHeaders(): array + { + return []; + } + /** * Send the response for this type of exception. * @@ -38,7 +48,7 @@ class NotifyException extends Exception implements Responsable // Front-end JSON handling. API-side handling managed via handler. if ($request->wantsJson()) { - return response()->json(['error' => $message], 403); + return response()->json(['error' => $message], $this->getStatusCode()); } if (!empty($message)) { diff --git a/app/Exceptions/PrettyException.php b/app/Exceptions/PrettyException.php index f446442d0..606085231 100644 --- a/app/Exceptions/PrettyException.php +++ b/app/Exceptions/PrettyException.php @@ -4,18 +4,12 @@ namespace BookStack\Exceptions; use Exception; use Illuminate\Contracts\Support\Responsable; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; -class PrettyException extends Exception implements Responsable +class PrettyException extends Exception implements Responsable, HttpExceptionInterface { - /** - * @var ?string - */ - protected $subtitle = null; - - /** - * @var ?string - */ - protected $details = null; + protected ?string $subtitle = null; + protected ?string $details = null; /** * Render a response for when this exception occurs. @@ -24,7 +18,7 @@ class PrettyException extends Exception implements Responsable */ public function toResponse($request) { - $code = ($this->getCode() === 0) ? 500 : $this->getCode(); + $code = $this->getStatusCode(); return response()->view('errors.' . $code, [ 'message' => $this->getMessage(), @@ -46,4 +40,20 @@ class PrettyException extends Exception implements Responsable return $this; } + + /** + * Get the desired HTTP status code for this exception. + */ + public function getStatusCode(): int + { + return ($this->getCode() === 0) ? 500 : $this->getCode(); + } + + /** + * Get the desired HTTP headers for this exception. + */ + public function getHeaders(): array + { + return []; + } } diff --git a/tests/Api/PagesApiTest.php b/tests/Api/PagesApiTest.php index 12b38bc07..75cc2807f 100644 --- a/tests/Api/PagesApiTest.php +++ b/tests/Api/PagesApiTest.php @@ -159,6 +159,27 @@ class PagesApiTest extends TestCase $this->assertStringContainsString('testing', $html); } + public function test_read_endpoint_returns_not_found() + { + $this->actingAsApiEditor(); + // get an id that is not used + $id = Page::orderBy('id', 'desc')->first()->id + 1; + $this->assertNull(Page::find($id)); + + $resp = $this->getJson($this->baseEndpoint . "/$id"); + + $resp->assertNotFound(); + $this->assertNull($resp->json('id')); + $resp->assertJsonIsObject('error'); + $resp->assertJsonStructure([ + 'error' => [ + 'code', + 'message', + ], + ]); + $this->assertSame(404, $resp->json('error')['code']); + } + public function test_update_endpoint() { $this->actingAsApiEditor();