Symfony 4 扩展验证请求

Symfony 4 extend request for validation

我目前正在做一个 API 项目。我习惯了 Laravel,现在我需要使用 Symfony。我想像 Laravel 那样使用请求进行验证。 所以我扩展了 Symfony\Component\HttpFoundation\Request class。在那里我做了一些逻辑来检查和清理传入的请求。

之后,我将新创建的请求添加到控制器中的存储函数中。但这给了我一个错误:

Argument 1 passed to App\Controller\Ticket\TicketController::store() must be an instance of App\Validation\Ticket\TicketStoreRequest, instance of Symfony\Component\HttpFoundation\Request given,/vendor/symfony/http-kernel/HttpKernel.php on line 149 {"exception":"[object] (Symfony\Component\Debug\Exception\FatalThrowableError(code: 0): Argument 1 passed to App\Controller\Ticket\TicketController::store() must be an instance of App\Validation\Ticket\TicketStoreRequest, instance of Symfony\Component\HttpFoundation\Request given

谷歌搜索后,我找到了几个选项。

  1. 将每个控制器操作添加到 service.yaml
  2. 创建侦听器并在正确的路由上添加验证

但是所有选项都需要在不同地方提供额外信息。我希望有人有更好的主意。

我发现了一些可能为您指明正确方向的提示:

Symfony ships with five value resolvers in the HttpKernel component:

...

RequestValueResolver

Injects the current Request if type-hinted with Request or a class extending Request.

https://symfony.com/doc/current/controller/argument_value_resolver.html

然后页面继续描述自定义 RequestAttributeValueResolver 的实现。此自定义解析器可以在您的 services.yml。 尽管在此示例中为一个单一属性类型(用户)创建了一个 Class,但有一些方法可以创建更动态的实现。

在此示例中,ArgumentMetadata 参数具有一个方法 $argument->getType(),该方法应包含要检查的类型的字符串表示形式:

if (User::class !== $argument->getType()) {
    return false;
}

没有什么能阻止您检查一组受支持的请求类型。该数组可以作为自定义 RequestValueResolver 中的 class 成员进行管理。自定义 RequestValueResolver class 的唯一要求是 supports() 方法 returns true 用于支持的请求类型,以及 resolve() 函数returns 此支持的请求类型的一个实例。这应该很简单,因为这两种方法都通过 ArgumentMetaData 参数提供了 'desired' class。

作为替代方案,您可以为每个要支持的自定义请求类型实现自定义 RequestValueResolver,但这感觉不是很优雅。

我不能保证这会起作用,我也不确定在示例中实现 RequestAttributeValueResolver 和实现自定义 RequestValueResolver 之间的区别,但我觉得它可能只是工作,用一点肘部润滑脂。

供参考:https://api.symfony.com/4.1/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.html

这是一个详细说明我的另一个示例的解决方案。它不像我的另一个示例那样安全,但它满足您的要求。

public function supports(Request $request, ArgumentMetadata $argument)
{    
    $desiredRequestClass = $argument->getType();

    return class_exists($desiredRequestClass);
}

public function resolve(Request $request, ArgumentMetadata $argument)
{
    $desiredRequestClass = $argument->getType();
    $customRequest = new $desiredRequestClass();

    $customRequest->createFromGlobals();
    // TODO: more initialization you might need.

    yield $customRequest;
}

可能建议检查 $desiredRequestClass 是否是 Request 的后代。