Skip to content

Commit a25d919

Browse files
committed
#[MapRequestPayload][Serializer] improve nested payload validation for MapRequestPayload using a new serialization context
1 parent aeb2489 commit a25d919

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

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

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,50 @@ public function testValidationNotPerformedWhenPartialDenormalizationReturnsViola
285285
}
286286
}
287287

288+
public function testNestedPayloadErrorReportingWhenPartialDenormalizationReturnsViolation()
289+
{
290+
$content = '{
291+
"name": "john doe",
292+
"address": {
293+
"address": "2332 street",
294+
"zipcode": "20220",
295+
"city": "Paris",
296+
"country": "75000",
297+
"geolocalization": {
298+
"lng": 32.423
299+
}
300+
}
301+
}';
302+
$serializer = new Serializer([new ObjectNormalizer()], ['json' => new JsonEncoder()]);
303+
304+
$validator = $this->createMock(ValidatorInterface::class);
305+
$validator->expects($this->never())
306+
->method('validate');
307+
308+
$resolver = new RequestPayloadValueResolver($serializer, $validator);
309+
$request = Request::create('/', 'POST', server: ['CONTENT_TYPE' => 'application/json'], content: $content);
310+
$kernel = $this->createMock(HttpKernelInterface::class);
311+
312+
// Test using use_class_as_default_expected_type context
313+
$argument = new ArgumentMetadata('invalid-nested-payload', Employee::class, false, false, null, false, [
314+
MapRequestPayload::class => new MapRequestPayload(serializationContext: ['use_class_as_default_expected_type' => true]),
315+
]);
316+
$arguments = $resolver->resolve($request, $argument);
317+
$event = new ControllerArgumentsEvent($kernel, function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST);
318+
319+
try {
320+
$resolver->onKernelControllerArguments($event);
321+
$this->fail(sprintf('Expected "%s" to be thrown.', HttpException::class));
322+
} catch (HttpException $e) {
323+
$validationFailedException = $e->getPrevious();
324+
$this->assertInstanceOf(ValidationFailedException::class, $validationFailedException);
325+
$this->assertSame(
326+
sprintf('This value should be of type %s.', Geolocalization::class),
327+
$validationFailedException->getViolations()[0]->getMessage()
328+
);
329+
}
330+
}
331+
288332
public function testUnsupportedMedia()
289333
{
290334
$serializer = new Serializer();
@@ -731,3 +775,34 @@ public function getPassword(): string
731775
return $this->password;
732776
}
733777
}
778+
779+
class Employee
780+
{
781+
public function __construct(
782+
public string $name,
783+
#[Assert\Valid]
784+
public ?Address $address = null,
785+
) {
786+
}
787+
}
788+
789+
class Address
790+
{
791+
public function __construct(
792+
public string $address,
793+
public string $zipcode,
794+
public string $city,
795+
public string $country,
796+
public Geolocalization $geolocalization,
797+
) {
798+
}
799+
}
800+
801+
class Geolocalization
802+
{
803+
public function __construct(
804+
public string $lat,
805+
public string $lng,
806+
) {
807+
}
808+
}

src/Symfony/Component/Serializer/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
CHANGELOG
22
=========
33

4+
7.1
5+
---
6+
* Add `AbstractNormalizer::USE_CLASS_AS_DEFAULT_EXPECTED_TYPE` in order to use the FQCN as the default value for NotNormalizableValueException's expectedTypes instead of unknown
7+
48
7.0
59
---
610

src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
118118
*/
119119
public const REQUIRE_ALL_PROPERTIES = 'require_all_properties';
120120

121+
/**
122+
* Use class name as default expected type when throwing NotNormalizableValueException instead of unknown
123+
*/
124+
public const USE_CLASS_AS_DEFAULT_EXPECTED_TYPE = 'use_class_as_default_expected_type';
125+
121126
/**
122127
* @internal
123128
*/
@@ -380,7 +385,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
380385
$exception = NotNormalizableValueException::createForUnexpectedDataType(
381386
sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name),
382387
$data,
383-
['unknown'],
388+
[isset($context[self::USE_CLASS_AS_DEFAULT_EXPECTED_TYPE]) ? $class : 'unknown'],
384389
$context['deserialization_path'] ?? null,
385390
true
386391
);

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