From 0af85fe7e77b264fe83c8008693ef742fc42083b Mon Sep 17 00:00:00 2001 From: Jeroeny Date: Wed, 18 Oct 2023 10:56:22 +0200 Subject: [PATCH] Fix RequestPayloadValueResolver handling error with no ExpectedTypes --- .../RequestPayloadValueResolver.php | 8 +++-- .../RequestPayloadValueResolverTest.php | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php index 38ee7758a70b6..1179f7c098bb5 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php @@ -108,11 +108,15 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo } catch (PartialDenormalizationException $e) { $trans = $this->translator ? $this->translator->trans(...) : fn ($m, $p) => strtr($m, $p); foreach ($e->getErrors() as $error) { - $parameters = ['{{ type }}' => implode('|', $error->getExpectedTypes())]; + $parameters = []; + $template = 'This value was of an unexpected type.'; + if ($expectedTypes = $error->getExpectedTypes()) { + $template = 'This value should be of type {{ type }}.'; + $parameters['{{ type }}'] = implode('|', $expectedTypes); + } if ($error->canUseMessageForUser()) { $parameters['hint'] = $error->getMessage(); } - $template = 'This value should be of type {{ type }}.'; $message = $trans($template, $parameters, 'validators'); $violations->add(new ConstraintViolation($message, $template, $parameters, null, $error->getPath(), null)); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php index 179f14a1271e8..dcf6c8dcaa006 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php @@ -23,9 +23,12 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Encoder\XmlEncoder; +use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Exception\PartialDenormalizationException; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\Exception\ValidationFailedException; @@ -332,6 +335,34 @@ public function testRequestContentValidationPassed() $this->assertEquals([$payload], $event->getArguments()); } + /** + * @testWith [null] + * [[]] + */ + public function testRequestContentWithUntypedErrors(?array $types) + { + $this->expectException(HttpException::class); + $this->expectExceptionMessage('This value was of an unexpected type.'); + $serializer = $this->createMock(SerializerDenormalizer::class); + + if (null === $types) { + $exception = new NotNormalizableValueException('Error with no types'); + } else { + $exception = NotNormalizableValueException::createForUnexpectedDataType('Error with no types', '', []); + } + $serializer->method('deserialize')->willThrowException(new PartialDenormalizationException([], [$exception])); + + $resolver = new RequestPayloadValueResolver($serializer, $this->createMock(ValidatorInterface::class)); + $request = Request::create('/', 'POST', server: ['CONTENT_TYPE' => 'application/json'], content: '{"price": 50}'); + + $arguments = $resolver->resolve($request, new ArgumentMetadata('valid', RequestPayload::class, false, false, null, false, [ + MapRequestPayload::class => new MapRequestPayload(), + ])); + $event = new ControllerArgumentsEvent($this->createMock(HttpKernelInterface::class), function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST); + + $resolver->onKernelControllerArguments($event); + } + public function testQueryStringValidationPassed() { $payload = new RequestPayload(50); @@ -638,6 +669,10 @@ public function __construct(public readonly float $price) } } +interface SerializerDenormalizer extends SerializerInterface, DenormalizerInterface +{ +} + class User { public function __construct(