对于一个API项目,如果输入数据不正确,我希望在调度控制器之前很早就失败。我已经设法通过使用路由默认值和订阅KernelEvents::REQUEST
事件的事件订阅者来完成验证。
正如您在代码中看到的,我试图从容器中获取验证器。我的假设是,因为auto wire是on的,它应该找到它们,但是has()
永远不会返回true
。
那么我到底做错了什么呢?我正在尝试自动解析验证器,我希望不必在服务中显式声明它。yml文件。
控制器(摘录):
#[Route('/sign-up', name: 'sign_up', defaults: ['_validator' => SignUpValidator::class],methods: ['POST'])]
public function register(int $page): Response
{
return new Response();
}
事件订阅者(摘录):
public function __construct(
protected readonly Container $container
) {}
protected function resolveValidator(Request $request): ?RequestValidatorInterface
{
$validatorClass = (string)$request->attributes->get('_validator');
if ($this->container->has($validatorClass)) {
return $this->container->get($validatorClass);
}
return null;
}
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
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/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
PsrContainerContainerInterface: '@service_container'
SymfonyComponentDependencyInjectionContainer: '@service_container'
虽然不再可能从主服务容器中获取私有服务,但您可以选择将其设置为公共服务。当然,这将需要额外的开发人员工作,但这将防止它被自动删除/内联,并将使其直接在服务容器中可用。
顺便说一下,只有当服务被定义为控制器/方法的参数时,自动装配才有效。在你的例子中,Symfony在决定注入哪个服务时不会考虑Route注释的默认值。如果您不想让这些类型的验证器服务公开,一个可能的方法是创建一个包含所有可能的验证器服务的自定义服务定位器。您可以为这些服务创建一个服务标签(例如app.request_validator
),并使用一个标记的接口(RequestValidatorInterface
)来自动配置它们。这将允许您通过一个单一的、统一的接口管理和访问所有验证器服务:
class SignUpValidator implements RequestValidatorInterface
{
// ...
}
在任何DI扩展上下文中:
$container->registerForAutoconfiguration(RequestValidatorInterface::class)
->addTag('app.request_validator');
然后,可以用这个小定位器注入事件订阅器:
AppEventSubscriberRequestValidatorSubscriber:
arguments:
- !tagged_locator 'app.request_validator'
RequestValidatorSubscriber
可以使用PsrContainerContainerInterface
作为此定位器参数的类型。