Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3b6d372

Browse filesBrowse files
feature #50767 [HttpKernel] RequestPayloadValueResolver Add support for custom http status code (zim32)
This PR was merged into the 6.4 branch. Discussion ---------- [HttpKernel] RequestPayloadValueResolver Add support for custom http status code | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | no | License | MIT | Doc PR | Will do if accepted Reading this discussion #49134 (comment) it looks like a good idea to have ability to customize http status code in case of a validation error. This MR does not create any BC's. **MapQueryString** and **MapRequestPayload** attributes now have additional parameter `validationFailedStatusCode`, which allows to specify which http status code will be used if validation fails. Commits ------- 4d118c0 [HttpKernel] RequestPayloadValueResolver Add support for custom http status code
2 parents 3b34568 + 4d118c0 commit 3b6d372
Copy full SHA for 3b6d372

File tree

5 files changed

+83
-3
lines changed
Filter options

5 files changed

+83
-3
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Attribute/MapQueryString.php
+2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Attribute/MapRequestPayload.php
+2Lines 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/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* `BundleInterface` no longer extends `ContainerAwareInterface`
88
* Add optional `$className` parameter to `ControllerEvent::getAttributes()`
99
* Add native return types to `TraceableEventDispatcher` and to `MergeExtensionConfigurationPass`
10+
* Add argument `$validationFailedStatusCode` to `#[MapQueryString]` and `#[MapRequestPayload]`
1011

1112
6.3
1213
---

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php
+2-2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php
+76-1Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.