Skip to content

Commit 45ce17a

Browse files
zim32nicolas-grekas
authored andcommitted
[HttpKernel] RequestPayloadValueResolver Add support for custom http status code
1 parent 2fe2b5a commit 45ce17a

File tree

4 files changed

+82
-3
lines changed

4 files changed

+82
-3
lines changed

src/Symfony/Component/HttpKernel/Attribute/MapQueryString.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\HttpKernel\Attribute;
1313

14+
use Symfony\Component\HttpFoundation\Response;
1415
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestPayloadValueResolver;
1516
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
1617
use Symfony\Component\Validator\Constraints\GroupSequence;
@@ -29,6 +30,7 @@ public function __construct(
2930
public readonly array $serializationContext = [],
3031
public readonly string|GroupSequence|array|null $validationGroups = null,
3132
string $resolver = RequestPayloadValueResolver::class,
33+
public readonly int $validationFailedStatusCode = Response::HTTP_NOT_FOUND,
3234
) {
3335
parent::__construct($resolver);
3436
}

src/Symfony/Component/HttpKernel/Attribute/MapRequestPayload.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\HttpKernel\Attribute;
1313

14+
use Symfony\Component\HttpFoundation\Response;
1415
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestPayloadValueResolver;
1516
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
1617
use Symfony\Component\Validator\Constraints\GroupSequence;
@@ -30,6 +31,7 @@ public function __construct(
3031
public readonly array $serializationContext = [],
3132
public readonly string|GroupSequence|array|null $validationGroups = null,
3233
string $resolver = RequestPayloadValueResolver::class,
34+
public readonly int $validationFailedStatusCode = Response::HTTP_UNPROCESSABLE_ENTITY,
3335
) {
3436
parent::__construct($resolver);
3537
}

src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo
8888
foreach ($arguments as $i => $argument) {
8989
if ($argument instanceof MapQueryString) {
9090
$payloadMapper = 'mapQueryString';
91-
$validationFailedCode = Response::HTTP_NOT_FOUND;
91+
$validationFailedCode = $argument->validationFailedStatusCode;
9292
} elseif ($argument instanceof MapRequestPayload) {
9393
$payloadMapper = 'mapRequestPayload';
94-
$validationFailedCode = Response::HTTP_UNPROCESSABLE_ENTITY;
94+
$validationFailedCode = $argument->validationFailedStatusCode;
9595
} else {
9696
continue;
9797
}

src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public function testNullableValueArgument()
124124

125125
$resolver->onKernelControllerArguments($event);
126126

127-
$this->assertEquals([null], $event->getArguments());
127+
$this->assertSame([null], $event->getArguments());
128128
}
129129

130130
public function testQueryNullableValueArgument()
@@ -251,6 +251,7 @@ public function testValidationNotPassed()
251251
$this->fail(sprintf('Expected "%s" to be thrown.', HttpException::class));
252252
} catch (HttpException $e) {
253253
$validationFailedException = $e->getPrevious();
254+
$this->assertSame(404, $e->getStatusCode());
254255
$this->assertInstanceOf(ValidationFailedException::class, $validationFailedException);
255256
$this->assertSame('This value should be of type unknown.', $validationFailedException->getViolations()[0]->getMessage());
256257
$this->assertSame('Test', $validationFailedException->getViolations()[1]->getMessage());
@@ -601,6 +602,73 @@ public static function provideValidationGroupsOnManyTypes(): iterable
601602
new MapQueryString(validationGroups: new Assert\GroupSequence(['strict'])),
602603
];
603604
}
605+
606+
public function testQueryValidationErrorCustomStatusCode()
607+
{
608+
$serializer = new Serializer([new ObjectNormalizer()], []);
609+
610+
$validator = $this->createMock(ValidatorInterface::class);
611+
612+
$validator->expects($this->once())
613+
->method('validate')
614+
->willReturn(new ConstraintViolationList([new ConstraintViolation('Page is invalid', null, [], '', null, '')]));
615+
616+
$resolver = new RequestPayloadValueResolver($serializer, $validator);
617+
618+
$argument = new ArgumentMetadata('page', QueryPayload::class, false, false, null, false, [
619+
MapQueryString::class => new MapQueryString(validationFailedStatusCode: 400),
620+
]);
621+
$request = Request::create('/?page=123');
622+
623+
$kernel = $this->createMock(HttpKernelInterface::class);
624+
$arguments = $resolver->resolve($request, $argument);
625+
$event = new ControllerArgumentsEvent($kernel, function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST);
626+
627+
try {
628+
$resolver->onKernelControllerArguments($event);
629+
$this->fail(sprintf('Expected "%s" to be thrown.', HttpException::class));
630+
} catch (HttpException $e) {
631+
$validationFailedException = $e->getPrevious();
632+
$this->assertSame(400, $e->getStatusCode());
633+
$this->assertInstanceOf(ValidationFailedException::class, $validationFailedException);
634+
$this->assertSame('Page is invalid', $validationFailedException->getViolations()[0]->getMessage());
635+
}
636+
}
637+
638+
public function testRequestPayloadValidationErrorCustomStatusCode()
639+
{
640+
$content = '{"price": 50, "title": ["not a string"]}';
641+
$payload = new RequestPayload(50);
642+
$serializer = new Serializer([new ObjectNormalizer()], ['json' => new JsonEncoder()]);
643+
644+
$validator = $this->createMock(ValidatorInterface::class);
645+
$validator->expects($this->once())
646+
->method('validate')
647+
->with($payload)
648+
->willReturn(new ConstraintViolationList([new ConstraintViolation('Test', null, [], '', null, '')]));
649+
650+
$resolver = new RequestPayloadValueResolver($serializer, $validator);
651+
652+
$argument = new ArgumentMetadata('invalid', RequestPayload::class, false, false, null, false, [
653+
MapRequestPayload::class => new MapRequestPayload(validationFailedStatusCode: 400),
654+
]);
655+
$request = Request::create('/', 'POST', server: ['CONTENT_TYPE' => 'application/json'], content: $content);
656+
657+
$kernel = $this->createMock(HttpKernelInterface::class);
658+
$arguments = $resolver->resolve($request, $argument);
659+
$event = new ControllerArgumentsEvent($kernel, function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST);
660+
661+
try {
662+
$resolver->onKernelControllerArguments($event);
663+
$this->fail(sprintf('Expected "%s" to be thrown.', HttpException::class));
664+
} catch (HttpException $e) {
665+
$validationFailedException = $e->getPrevious();
666+
$this->assertSame(400, $e->getStatusCode());
667+
$this->assertInstanceOf(ValidationFailedException::class, $validationFailedException);
668+
$this->assertSame('This value should be of type unknown.', $validationFailedException->getViolations()[0]->getMessage());
669+
$this->assertSame('Test', $validationFailedException->getViolations()[1]->getMessage());
670+
}
671+
}
604672
}
605673

606674
class RequestPayload
@@ -612,3 +680,10 @@ public function __construct(public readonly float $price)
612680
{
613681
}
614682
}
683+
684+
class QueryPayload
685+
{
686+
public function __construct(public readonly float $page)
687+
{
688+
}
689+
}

0 commit comments

Comments
 (0)
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