From 98f4fa1bd9f96772fef2740d69a5d11293e3819c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 26 Dec 2023 11:22:59 +0100 Subject: [PATCH] [HttpKernel] Add `HttpException::fromStatusCode()` --- src/Symfony/Component/HttpKernel/CHANGELOG.md | 1 + .../RequestPayloadValueResolver.php | 19 +++++------ .../EventListener/ErrorListener.php | 4 +-- .../HttpKernel/Exception/HttpException.php | 21 ++++++++++++ .../Tests/Exception/HttpExceptionTest.php | 32 +++++++++++++++++++ 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index eb0e3c1cb44e0..45761dfbbed42 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add method `isKernelTerminating()` to `ExceptionEvent` that allows to check if an exception was thrown while the kernel is being terminated + * Add `HttpException::fromStatusCode()` 7.0 --- diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php index 444be1b3fe7d2..85e94c235de12 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php @@ -13,13 +13,14 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryString; use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Serializer\Exception\NotEncodableValueException; use Symfony\Component\Serializer\Exception\PartialDenormalizationException; @@ -124,13 +125,13 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo } if (\count($violations)) { - throw new HttpException($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), iterator_to_array($violations))), new ValidationFailedException($payload, $violations)); + throw HttpException::fromStatusCode($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), iterator_to_array($violations))), new ValidationFailedException($payload, $violations)); } } else { try { $payload = $this->$payloadMapper($request, $type, $argument); } catch (PartialDenormalizationException $e) { - throw new HttpException($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), $e->getErrors())), $e); + throw HttpException::fromStatusCode($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), $e->getErrors())), $e); } } @@ -138,7 +139,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo $payload = match (true) { $argument->metadata->hasDefaultValue() => $argument->metadata->getDefaultValue(), $argument->metadata->isNullable() => null, - default => throw new HttpException($validationFailedCode) + default => throw HttpException::fromStatusCode($validationFailedCode) }; } @@ -167,11 +168,11 @@ private function mapQueryString(Request $request, string $type, MapQueryString $ private function mapRequestPayload(Request $request, string $type, MapRequestPayload $attribute): ?object { if (null === $format = $request->getContentTypeFormat()) { - throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, 'Unsupported format.'); + throw new UnsupportedMediaTypeHttpException('Unsupported format.'); } if ($attribute->acceptFormat && !\in_array($format, (array) $attribute->acceptFormat, true)) { - throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, sprintf('Unsupported format, expects "%s", but "%s" given.', implode('", "', (array) $attribute->acceptFormat), $format)); + throw new UnsupportedMediaTypeHttpException(sprintf('Unsupported format, expects "%s", but "%s" given.', implode('", "', (array) $attribute->acceptFormat), $format)); } if ($data = $request->request->all()) { @@ -183,15 +184,15 @@ private function mapRequestPayload(Request $request, string $type, MapRequestPay } if ('form' === $format) { - throw new HttpException(Response::HTTP_BAD_REQUEST, 'Request payload contains invalid "form" data.'); + throw new BadRequestHttpException('Request payload contains invalid "form" data.'); } try { return $this->serializer->deserialize($data, $type, $format, self::CONTEXT_DESERIALIZE + $attribute->serializationContext); } catch (UnsupportedFormatException $e) { - throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, sprintf('Unsupported format: "%s".', $format), $e); + throw new UnsupportedMediaTypeHttpException(sprintf('Unsupported format: "%s".', $format), $e); } catch (NotEncodableValueException $e) { - throw new HttpException(Response::HTTP_BAD_REQUEST, sprintf('Request payload contains invalid "%s" data.', $format), $e); + throw new BadRequestHttpException(sprintf('Request payload contains invalid "%s" data.', $format), $e); } } } diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php index cf52851928f86..6e47baf2ee6c3 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php @@ -63,7 +63,7 @@ public function logKernelException(ExceptionEvent $event): void } if (!$throwable instanceof HttpExceptionInterface || $throwable->getStatusCode() !== $config['status_code']) { $headers = $throwable instanceof HttpExceptionInterface ? $throwable->getHeaders() : []; - $throwable = new HttpException($config['status_code'], $throwable->getMessage(), $throwable, $headers); + $throwable = HttpException::fromStatusCode($config['status_code'], $throwable->getMessage(), $throwable, $headers); $event->setThrowable($throwable); } break; @@ -78,7 +78,7 @@ public function logKernelException(ExceptionEvent $event): void /** @var WithHttpStatus $instance */ $instance = $attributes[0]->newInstance(); - $throwable = new HttpException($instance->statusCode, $throwable->getMessage(), $throwable, $instance->headers); + $throwable = HttpException::fromStatusCode($instance->statusCode, $throwable->getMessage(), $throwable, $instance->headers); $event->setThrowable($throwable); break; } diff --git a/src/Symfony/Component/HttpKernel/Exception/HttpException.php b/src/Symfony/Component/HttpKernel/Exception/HttpException.php index f95f77bcafae9..7eaf049e9302c 100644 --- a/src/Symfony/Component/HttpKernel/Exception/HttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/HttpException.php @@ -29,6 +29,27 @@ public function __construct(int $statusCode, string $message = '', \Throwable $p parent::__construct($message, $code, $previous); } + public static function fromStatusCode(int $statusCode, string $message = '', \Throwable $previous = null, array $headers = [], int $code = 0): self + { + return match ($statusCode) { + 400 => new BadRequestHttpException($message, $previous, $code, $headers), + 403 => new AccessDeniedHttpException($message, $previous, $code, $headers), + 404 => new NotFoundHttpException($message, $previous, $code, $headers), + 406 => new NotAcceptableHttpException($message, $previous, $code, $headers), + 409 => new ConflictHttpException($message, $previous, $code, $headers), + 410 => new GoneHttpException($message, $previous, $code, $headers), + 411 => new LengthRequiredHttpException($message, $previous, $code, $headers), + 412 => new PreconditionFailedHttpException($message, $previous, $code, $headers), + 423 => new LockedHttpException($message, $previous, $code, $headers), + 415 => new UnsupportedMediaTypeHttpException($message, $previous, $code, $headers), + 422 => new UnprocessableEntityHttpException($message, $previous, $code, $headers), + 428 => new PreconditionRequiredHttpException($message, $previous, $code, $headers), + 429 => new TooManyRequestsHttpException(null, $message, $previous, $code, $headers), + 503 => new ServiceUnavailableHttpException(null, $message, $previous, $code, $headers), + default => new static($statusCode, $message, $previous, $headers, $code), + }; + } + public function getStatusCode(): int { return $this->statusCode; diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php index fad9e796f439b..11636bbb60bd5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php @@ -63,6 +63,38 @@ public function testThrowableIsAllowedForPrevious() $this->assertSame($previous, $exception->getPrevious()); } + /** + * @dataProvider provideStatusCode + */ + public function testFromStatusCode(int $statusCode) + { + $exception = HttpException::fromStatusCode($statusCode); + $this->assertInstanceOf(HttpException::class, $exception); + $this->assertSame($statusCode, $exception->getStatusCode()); + } + + public static function provideStatusCode() + { + return [ + [400], + [401], + [403], + [404], + [406], + [409], + [410], + [411], + [412], + [418], + [423], + [415], + [422], + [428], + [429], + [503], + ]; + } + protected function createException(string $message = '', \Throwable $previous = null, int $code = 0, array $headers = []): HttpException { return new HttpException(200, $message, $previous, $headers, $code); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy