8000 [HttpKernel] RequestPayloadValueResolver Add support for custom http … · symfony/symfony@45ce17a · GitHub
[go: up one dir, main page]

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