Symfony的 MapQueryString不支持可选参数

后端开发 2026-07-12

我有一个简单的端点,带有用于过滤的可选查询参数。在我的控制器中,我使用MapQueryString将它们映射到一个DTO,并在内部进行验证。问题在于当我完全不传任何参数调用端点时,Symfony会返回422 HTTP错误。

我的控制器端点:

#[SuccessfulPaginatedResponse(description: 'Returns paginated list of categories by given filters.')]
#[ValidationErrorResponse]
#[Route(name: 'categories_index', methods: ['GET'], format: 'json')]
public function index(
    #[MapQueryString(
        validationGroups: ['default'],
        validationFailedStatusCode: Response::HTTP_UNPROCESSABLE_ENTITY
    )] IndexRequestQueryDto $queryParams,
    Request $request
): JSONResponse {
    $filter = CategoryFilterFactory::createFromIndexRequest($queryParams);
    $result = $this->categoryService->fetch($filter);

    $paginatedResponse = PaginatedResponse::create(
        $result['data'],
        $queryParams,
        $result['totalCount'],
        $request
    );

    return $this->json($paginatedResponse->toArray());
}

我的DTO:

class IndexRequestQueryDto extends PaginatedRequest
{
    public function __construct(
        #[Assert\Positive]
        #[Assert\Type('integer')]
        public readonly ?int $businessUnit = null,
        #[Assert\Positive]
        #[Assert\Type('integer')]
        public readonly ?int $parentId = null,
        #[Assert\Type('string')]
        public readonly ?string $name = null,
        ?int $page = null,
        ?int $pageSize = null,
    ) {
        parent::__construct($page, $pageSize);
    }
}

我的DTO的扩展类:

class PaginatedRequest implements DtoInterface
{
    public const DEFAULT_PAGE = 1;
    public const DEFAULT_PAGE_SIZE = 25;
    public const MAX_PAGE_SIZE = 100;

    public function __construct(
        #[Assert\Type('integer')]
        #[Assert\Positive]
        public readonly ?int $page = null,
        #[Assert\Type('integer')]
        #[Assert\Positive]
        #[Assert\LessThanOrEqual(self::MAX_PAGE_SIZE)]
        public readonly ?int $pageSize = null,
    ) {
    }

我尝试但没奏效的做法:
- 移除了所有断言
- 在IndexRequestQueryDto中尝试定义page与 pageSize,但没有将它们传给父构造函数
- 尝试使用#[Assert\When()]

唯一对我有用的是在控制器中使用ValidatorInterface,但该方案需要额外的代码,我不喜欢:

$queryParams = new IndexRequestQueryDto(
    $request->query->has('businessUnit') ? (int) $request->query->get('businessUnit') : null,
    $request->query->has('parentId') ? (int) $request->query->get('parentId') : null,
    $request->query->has('name') ? (string) $request->query->get('name') : null,
    $request->query->has('page') ? (int) $request->query->get('page') : null,
    $request->query->has('pageSize') ? (int) $request->query->get('pageSize') : null,
);

$errors = $this->validator->validate($queryParams);

if (count($errors) > 0) {
    return $this->json(['errors' => $errors], Response::HTTP_UNPROCESSABLE_ENTITY);
}

Symfony版本为6.4,PHP为 8.2

解决方案

解决方案是在默认情况下不带参数来实例化Dto,感谢给出这个答案的人,不过他把答案删掉了。对我确实有用。

public function index(
    #[MapQueryString(
        validationFailedStatusCode: Response::HTTP_UNPROCESSABLE_ENTITY
    )] IndexRequestQueryDto $queryParams = new IndexRequestQueryDto(),
    Request $request
)
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章