Quantcast
Channel: Active questions tagged rest - Stack Overflow
Viewing all articles
Browse latest Browse all 3701

Unable to use a custom ValueResolver

$
0
0

I'm developing a Symfony 7 REST API. I'm following the official docs to implement a custom argument resolver to handle mapping of the request payload format that my endpoints are receiving to DTOs (https://symfony.com/doc/current/controller/value_resolver.html#adding-a-custom-value-resolver).

Here's an example of a request payload to an endpoint

{"article": {"title": "How to train your dragon","description": "Ever wonder how?","body": "You have to believe"  }}

Here's the custom ValueResolver

class NestedJsonValueResolver implements ValueResolverInterface{    public function __construct(        private readonly SerializerInterface $serializer,        private readonly ValidatorInterface $validator,    ) {    }    public function resolve(Request $request, ArgumentMetadata $argument): iterable    {        $argumentType = $argument->getType();        if (!in_array('NestedJsonDtoInterface', class_implements($argumentType))) {            return [];        }        return [$this->mapRequestPayload($request, $argumentType)];    }    /**     * @param Request $request     * @param class-string<NestedJsonDtoInterface> $type     */    private function mapRequestPayload(Request $request, string $type): ?object    {        if (null === $format = $request->getContentTypeFormat()) {            throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, 'Unsupported format.');        }        if ('' === $data = $request->getContent()) {            return null;        }        try {            $nestedJsonObjectKey = $type::getNestedJsonObjectKey();            $decodedData = json_decode($data, true);            $nestedJsonObject = $decodedData[$nestedJsonObjectKey];            $payload = $this->serializer->deserialize($nestedJsonObject, $type, 'json');            $violations = $this->validator->validate($payload);            if ($violations->count() > 0) {                throw new HttpException(Response::HTTP_UNPROCESSABLE_ENTITY, implode("\n", array_map(static fn ($e) => $e->getMessage(), iterator_to_array($violations))), new ValidationFailedException($payload, $violations));            }            return $payload;        } catch (UnsupportedFormatException $e) {            throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, sprintf('Unsupported format: "%s".', $format), $e);        } catch (NotEncodableValueException $e) {            throw new HttpException(Response::HTTP_BAD_REQUEST, sprintf('Request payload contains invalid "%s" data.', $format), $e);        }    }}

Here's my services.yaml

services:    # default configuration for services in *this* file    _defaults:        autowire: true      # Automatically injects dependencies in your services.        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.    # makes classes in src/ available to be used as services    # this creates a service per class whose id is the fully-qualified class name    App\:        resource: '../src/'        exclude:            - '../src/DependencyInjection/'            - '../src/Entity/'            - '../src/Kernel.php'    # add more service definitions when explicit configuration is needed    # please note that last definitions always *replace* previous ones    App\ValueResolver\NestedJsonValueResolver:        tags:            - controller.argument_value_resolver:                  name: nested_json                  priority: 150

Here's the controller action

    #[Route('', methods: ['POST'])]    public function store(        #[ValueResolver('nested_json')]        StoreArticleDto $dto    ): JsonResponse    {        $article = $this->articleService->store($dto);        return $this->json(['article' => $article,        ], 201);    }

And here's the target DTO

final readonly class StoreArticleDto implements NestedJsonDtoInterface{    private const NESTED_JSON_OBJECT_KEY = 'article';    public function __construct(        #[Assert\NotNull]        #[Assert\Type('string')]        private ?string $title,        #[Assert\NotNull]        #[Assert\Type('string')]        private ?string $body,        #[Assert\NotNull]        #[Assert\Type('string')]        private ?string $description,    )    {    }    public static function getNestedJsonObjectKey(): string    {        return self::NESTED_JSON_OBJECT_KEY;    }    public function getTitle(): ?string    {        return $this->title;    }    public function getBody(): ?string    {        return $this->body;    }    public function getDescription(): ?string    {        return $this->description;    }}

Upon hitting an endpoint the following Exception is being thrown.

enter image description here

How should I go about making the custom ValueResolver work?


Viewing all articles
Browse latest Browse all 3701

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>