如何将请求传递给DTO进行断言验证?

use*_*891 2 symfony symfony5

我正在努力实现symfony validation asset该要求。

我正在form-data从邮递员传递到控制器中的路线。

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

我创建了一个loginRequestDTO,如下所示:

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

但我收到以下错误。

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

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

小智 6

您可以编写自己的参数解析器,它将在控制器操作中注入任何实现的对象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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

interface DtoResolvedInterface
{

}
Run Code Online (Sandbox Code Playgroud)

并在您的 LoginRequest 中实现此接口

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


emo*_*iev 5

您需要在方法中使用Serializer 组件并反序列化 DTO 以使用数据创建对象。创建带有 header 的请求Content-Type: application/json并以 json 格式发送数据,而不是form-data

您的 DTO 应该这样声明:

use Symfony\Component\Validator\Constraints as Assert;

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

    /**
     * @Assert\NotBlank()
     */
   public string $password; 
   
}
Run Code Online (Sandbox Code Playgroud)

控制器方法:

    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);
        }

        // ...
    }
Run Code Online (Sandbox Code Playgroud)

  • 您可以注入序列化器服务而不是创建它 (3认同)