Symfony 7.X 自定义解析器始终被调用

问题描述 投票:0回答:1

目前我正在使用 Symfony 7.1 开发 API 我决定使用 DTO 对象来处理和验证我的请求的参数。

为了检测并报告请求中是否不存在强制参数,我创建了一个自定义解析器。

POST 请求

curl --location 'http://dev.myproject/api/v1/authentication/user' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: ••••••' \
--data-raw '{
    "username" : "username",
    "password": "myPassword"
}'

控制器

#[Route('/user', name: 'auth_user', methods: ['POST'], format: 'json')]
    public function authUser(
        #[MapRequestPayload(
            resolver: ApiAuthUserResolver::class
        )] ApiAuthUserDto $apiAuthUserDto,
    ): JsonResponse
    {
      [...]
    }

DTO 对象

namespace App\Dto\Api\Authentication;

use Symfony\Component\Validator\Constraints as Assert;

readonly class ApiAuthUserDto
{
    public function __construct(

        #[Assert\Type(
            type : 'string',
            message: 'Le champ username doit être du type string'
        )]
        #[Assert\NotBlank(message: 'Le champ username ne peut pas être vide')]
        public string $username,

        #[Assert\Type(
            type : 'string',
            message: 'Le champ password doit être du type string'
        )]
        #[Assert\NotBlank(message: 'Le champ password ne peut pas être vide')]
        public string $password,
    )
    {
    }
}

自定义解析器

namespace App\Resolver\Api;

use App\Dto\Api\Authentication\ApiAuthUserDto;
use App\Utils\Api\ApiParametersParser;
use App\Utils\Api\ApiParametersRef;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Validator\Validator\ValidatorInterface;

readonly class ApiAuthUserResolver implements ValueResolverInterface
{
    public function __construct(
        private ApiParametersParser $apiParametersParser,
        private ValidatorInterface  $validator
    ) {
    }

    /**
     * @param Request $request
     * @param ArgumentMetadata $argument
     * @return iterable
     */
    public function resolve(Request $request, ArgumentMetadata $argument): iterable
    {
        $data = json_decode($request->getContent(), true);
        $return = $this->apiParametersParser->parse(ApiParametersRef::PARAMS_REF_AUTH_USER, $data);

        if (!empty($return)) {
            throw new HttpException(Response::HTTP_FORBIDDEN,implode(',', $return));
        }

        $dto = new ApiAuthUserDto(
            $data['username'],
            $data['password']
        );

        $errors = $this->validator->validate($dto);
        if (count($errors) > 0) {
            $nb = $errors->count();
            $msg = [];
            for ($i = 0; $i < $nb; $i++) {
                $msg[] = $errors->get($i)->getMessage() . ' ';
            }
            throw new HttpException(Response::HTTP_FORBIDDEN,implode(',', $msg));
        }
        return [$dto];
    }
}

我设置了优先级,以免与其他解析器出现问题

  App\Resolver\Api\ApiAuthUserResolver:
    tags:
      - controller.argument_value_resolver:
          priority: 50

代码运行良好并正确完成其工作。

我的问题是这样的:

自从我为我的 API 实现了这个自定义解析器以来,我的应用程序中的所有路由都被破坏了,因为我的自定义解析器被系统地调用,原因我不知道。

例如这段代码,我的项目中的一个非常简单的路线,它调用 2 个对象

    #[Route('/dashboard/index', name: 'index')]
    #[Route('/dashboard', 'index_3')]
    #[Route('/', name: 'index_2')]
    public function index(DashboardTranslate $dashboardTranslate, UserDataService $userDataService): Response
    {[...]}

现在给我以下错误:

App\Utils\Api\ApiParametersParser::parse():参数 #2 ($apiParameters) 必须是数组类型,给定 null,在第 36 行的 \src\ValueResolver\Api\ApiAuthUserResolver.php 中调用

我不明白为什么它是系统地调用我的自定义解析器,即使它具有较低的优先级,并且我只是通过 MapRequestPayload 的解析器属性为操作定义它

我想做的是,这个自定义解析器仅在这种特定情况下使用,对于经典案例,Symfony 解析器的工作方式与之前的情况一样

我是否忘记了什么,错误配置了我的自定义解析器? 谢谢你的帮助

php symfony resolver symfony7
1个回答
0
投票

我想这与标签有关

- controller.argument_value_resolver:
      priority: 50

您添加的。这基本上告诉 symfony - 如果您在路由中遇到参数,请尝试使用最高优先级的参数值解析器来解析它,支持它

最后一部分是重要的。您需要一种方法来告诉框架,除了

AuthUser
之外,它不能将您的 ValueResolver 用于其他任何用途。正如文档中所声明的,您可以通过多种方式实现这一点。

如果这确实是问题所在,我建议您先尝试一下。您可以在

ApiAuthUserResolver::resolve
中指定初始条件。

这种情况看起来像这样:

public function resolve(Request $request, ArgumentMetadata $argument): iterable
    {
        $argumentType = $argument->getType();
        if (
            !$argumentType
            || !is_subclass_of($argumentType, ApiAuthUserDto::class, true)
        ) {
            return [];
        }

        // Rest of the function body as specified in question
    }

返回空数组告诉 symfony 使用不同的值解析器,优先级较低。希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.