Symfony 4 продлил запрос на проверку
В настоящее время я работаю над проектом API. Я привык к Laravel и теперь мне нужно работать с Symfony. Я хочу использовать запрос, как Laravel, для проверки. Поэтому я расширяю класс Symfony\Component\HttpFoundation\Request. Там я сделал некоторую логику, чтобы проверить и очистить входящий запрос.
После этого я добавляю свой недавно созданный запрос в функцию store в контроллере. Но это дает мне ошибку:
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
После некоторого поиска в Google я нашел несколько вариантов.
- Добавьте каждое действие контроллера в service.yaml
- Создайте слушателя и добавьте проверку правильности маршрута.
Но все варианты требуют дополнительной информации о другом месте. Я надеюсь, что у кого-то есть идея получше.
2 ответа
Я нашел несколько подсказок, которые могут указать вам правильное направление:
Symfony поставляется с пятью определителями значений в компоненте HttpKernel:
...
RequestValueResolver
Внедряет текущий запрос, если подсказывает тип с помощью запроса или класса, расширяющего запрос.
см. https://symfony.com/doc/current/controller/argument_value_resolver.html
Затем страница переходит к описанию реализации пользовательского RequestAttributeValueResolver. Этот пользовательский распознаватель может быть зарегистрирован в вашем services.yml
, Хотя в этом примере создается один класс для одного типа атрибута (пользователь), существуют способы создания более динамичной реализации.
В этом примере ArgumentMetadata
у параметра есть метод $argument->getType()
это должно содержать строковое представление типа, который проверяется на:
if (User::class !== $argument->getType()) {
return false;
}
Ничто не мешает вам проверять массив поддерживаемых типов запросов. Этот массив может управляться как член класса в вашем пользовательском RequestValueResolver
, Единственное требование к вашему заказу RequestValueResolver
класс, это то, что supports()
метод возвращает true
для поддерживаемых типов запросов, и что resolve()
функция возвращает экземпляр этого поддерживаемого типа запроса. Это должно быть просто, потому что оба метода передают "нужный" класс через ArgumentMetaData
параметр.
В качестве альтернативы, вы можете реализовать 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
,