symfony 如何将请求传递给DTO以进行Assert验证?

yvfmudvl  于 2022-11-16  发布在  其他
关注(0)|答案(3)|浏览(144)

我正在尝试对请求实现symfony validation asset
我将form-data从 Postman 传递到控制器中的路由。

#[Route(path: '/test', name: 'test', methods: 'GET')]
    public function login(LoginRequest $loginRequest): Response

我已经创建了一个loginRequest DTO,如下所示:

class LoginRequest
{
    public function __construct(
        /**
         * @Assert\NotBlank
         */
        public string $username,
        /**
         * @Assert\NotBlank
         */
        public string $password
    ) {
    }
}

但我得到以下错误。

Cannot autowire service "App\Dto\LoginRequest": argument "$username" of method "__construct()" is type-hinted "string", you should configure its value explicitly.

有人能帮助我如何使用DTO来验证参数请求吗?

mpgws1up

mpgws1up1#

您需要在方法中使用Serializer component并反序列化DTO,以便使用数据创建对象。
您的DTO应如下声明:

use Symfony\Component\Validator\Constraints as Assert;

class LoginRequest
{
    /**
     * @Assert\NotBlank()
     */
    public string $username;

    /**
     * @Assert\NotBlank()
     */
   public string $password; 
   
}

控制器方法:

use Symfony\Component\Serializer\SerializerInterface;

    #[Route(path: '/test', name: 'test', methods: 'GET')]
    public function login(Request  $request, SerializerInterface  $serializer, ValidatorInterface $validator): Response
    { 
        $dto = $serializer->deserialize($request->getContent(), LoginRequest::class, 'json');
        $errors = $validator->validate($dto);
        if (count($errors) > 0) {
            throw new BadRequestHttpException((string) $errors);
        }

        // ...
    }
zysjyyx4

zysjyyx42#

这是因为您的应用程序尝试使用DTO创建服务,并自动连接其构造器参数。
缺省服务容器配置如下

# config/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,Entity,Tests,Kernel.php}'

    # ...

要从服务容器中排除DTO,可以在名为Dto的文件夹中创建它,然后将此文件夹添加到配置文件的exclude行,如下所示。
exclude: '../src/{DependencyInjection,Entity,Tests,Dto,Kernel.php}'

6rqinv9w

6rqinv9w3#

您可以编写自己的Argument Resolver,它将在控制器操作中注入任何实现DtoResolvedInterface的对象

// src/ArgumentResolver/DtoValueResolver.php
class DtoValueResolver implements ArgumentValueResolverInterface
{
    public function __construct(
        private readonly DenormalizerInterface $denormalizer,
        private readonly ValidatorInterface $validator
    ) {
    }

    public function supports(Request $request, ArgumentMetadata $argument): bool
    {
        return $argument->getType() && str_starts_with($argument->getType(), DtoResolvedInterface::class);
    }

    public function resolve(Request $request, ArgumentMetadata $argument): iterable
    {
        try {
            $dtoValue = $this->denormalizer->denormalize($request->toArray(), $argument->getType());
        } catch (NotNormalizableValueException $e) {
            // wrong types
            throw new BadRequestHttpException();
        }

        $violations = $this->validator->validate($dtoValue);
        if ($violations->count() > 0) {
            // dto is not valid
            throw new BadRequestHttpException((string) $violations);
        }

        yield $dtoValue;
    }
}

声明任何空接口只是为了区分DTO类。

interface DtoResolvedInterface
{

}

并在LoginRequest中实现此接口

class LoginRequest implements DtoResolvedInterface
{
    public function __construct(
        /**
         * @Assert\NotBlank
         */
        public string $username,
        /**
         * @Assert\NotBlank
         */
        public string $password
    ) {
    }
}

相关问题