diff --git a/src/Laravel/ApiResource/Error.php b/src/Laravel/ApiResource/Error.php index 771dbdb8d60..a85c3c35793 100644 --- a/src/Laravel/ApiResource/Error.php +++ b/src/Laravel/ApiResource/Error.php @@ -136,6 +136,10 @@ public static function createFromException(\Exception|\Throwable $exception, int { $headers = ($exception instanceof SymfonyHttpExceptionInterface || $exception instanceof HttpExceptionInterface) ? $exception->getHeaders() : []; + if ($exception instanceof ProblemExceptionInterface) { + return new self($exception->getTitle(), $exception->getDetail(), $exception->getStatus(), $exception->getTrace(), $exception->getInstance(), $exception->getType(), $headers); + } + return new self('An error occurred', $exception->getMessage(), $status, $exception->getTrace(), type: '/errors/'.$status, headers: $headers); } diff --git a/src/Laravel/Tests/JsonProblemTest.php b/src/Laravel/Tests/JsonProblemTest.php index f2dc6cb09d8..43027506711 100644 --- a/src/Laravel/Tests/JsonProblemTest.php +++ b/src/Laravel/Tests/JsonProblemTest.php @@ -85,6 +85,20 @@ public function testRetrieveErrorHtml(): void ', $response->getContent()); } + public function testProblemExceptionInterface(): void + { + $response = $this->get('/api/teapot', headers: ['accept' => 'application/json']); + $response->assertStatus(418); + $response->assertHeader('content-type', 'application/problem+json; charset=utf-8'); + $response->assertJsonFragment([ + 'type' => '/problem/teapot', + 'title' => 'I\'m a teapot', + 'status' => 418, + 'detail' => 'No coffee here', + 'instance' => '/teapot', + ]); + } + /** * @return list}> */ diff --git a/src/Laravel/workbench/app/ApiResource/ServiceProvider.php b/src/Laravel/workbench/app/ApiResource/ServiceProvider.php index 81c2f032fbb..2ba78a05c59 100644 --- a/src/Laravel/workbench/app/ApiResource/ServiceProvider.php +++ b/src/Laravel/workbench/app/ApiResource/ServiceProvider.php @@ -16,7 +16,9 @@ use ApiPlatform\Metadata\Get; use Workbench\App\State\CustomProvider; use Workbench\App\State\CustomProviderWithDependency; +use Workbench\App\State\TeapotProvider; +#[Get(uriTemplate: 'teapot', name: 'teapot', provider: TeapotProvider::class)] #[Get(uriTemplate: 'custom_service_provider', provider: CustomProvider::class)] #[Get(uriTemplate: 'custom_service_provider_with_dependency', provider: CustomProviderWithDependency::class)] class ServiceProvider diff --git a/src/Laravel/workbench/app/Exceptions/TeapotException.php b/src/Laravel/workbench/app/Exceptions/TeapotException.php new file mode 100644 index 00000000000..ef9b2b088c8 --- /dev/null +++ b/src/Laravel/workbench/app/Exceptions/TeapotException.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Workbench\App\Exceptions; + +use ApiPlatform\Metadata\Exception\ProblemExceptionInterface; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; + +class TeapotException extends \Exception implements ProblemExceptionInterface, HttpExceptionInterface +{ + public function getStatusCode(): int + { + return 418; + } + + public function getHeaders(): array + { + return []; + } + + public function getType(): string + { + return '/problem/teapot'; + } + + public function getTitle(): ?string + { + return 'I\'m a teapot'; + } + + public function getStatus(): ?int + { + return 418; + } + + public function getDetail(): ?string + { + return 'No coffee here'; + } + + public function getInstance(): ?string + { + return '/teapot'; + } +} diff --git a/src/Laravel/workbench/app/State/TeapotProvider.php b/src/Laravel/workbench/app/State/TeapotProvider.php new file mode 100644 index 00000000000..1570d46adf9 --- /dev/null +++ b/src/Laravel/workbench/app/State/TeapotProvider.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Workbench\App\State; + +use ApiPlatform\Metadata\Operation; +use ApiPlatform\State\ProviderInterface; +use Workbench\App\ApiResource\ServiceProvider; +use Workbench\App\Exceptions\TeapotException; + +/** + * @implements ProviderInterface + */ +class TeapotProvider implements ProviderInterface +{ + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + throw new TeapotException('I\'m boiling'); + } +} diff --git a/src/State/ApiResource/Error.php b/src/State/ApiResource/Error.php index 09c034c3d87..ebee62fc084 100644 --- a/src/State/ApiResource/Error.php +++ b/src/State/ApiResource/Error.php @@ -199,6 +199,10 @@ public static function createFromException(\Exception|\Throwable $exception, int { $headers = ($exception instanceof SymfonyHttpExceptionInterface || $exception instanceof HttpExceptionInterface) ? $exception->getHeaders() : []; + if ($exception instanceof ProblemExceptionInterface) { + return new self($exception->getTitle(), $exception->getDetail(), $exception->getStatus(), $exception->getTrace(), $exception->getInstance(), $exception->getType(), $headers); + } + return new self('An error occurred', $exception->getMessage(), $status, $exception->getTrace(), type: "/errors/$status", headers: $headers, previous: $exception->getPrevious()); } diff --git a/tests/Functional/Parameters/StrictParametersTest.php b/tests/Functional/Parameters/StrictParametersTest.php index 3f918d8ed48..3a973b8338c 100644 --- a/tests/Functional/Parameters/StrictParametersTest.php +++ b/tests/Functional/Parameters/StrictParametersTest.php @@ -34,7 +34,7 @@ public static function getResources(): array public function testErrorAsParameterIsNotAllowed(): void { self::createClient()->request('GET', 'strict_query_parameters?bar=test'); - $this->assertJsonContains(['detail' => 'Parameter not supported']); + $this->assertJsonContains(['detail' => 'Parameter "bar" not supported']); $this->assertResponseStatusCodeSame(400); }