在Symfony控制器中使用自定义验证器

在Symfony控制器中使用自定义验证器,symfony,Symfony,我想对控制器中的各种查询参数使用自定义验证器。文件给出了以下示例: // validate a query parameter (a hash in this case) $incomingHashConstraint = new CustomAssert\IncomingHash(); // use the validator to validate the value // If you're using the new 2.5 validation API (y

我想对控制器中的各种查询参数使用自定义验证器。文件给出了以下示例:

 // validate a query parameter (a hash in this case)
    $incomingHashConstraint = new CustomAssert\IncomingHash();

    // use the validator to validate the value
    // If you're using the new 2.5 validation API (you probably are!)
    $errorList = $this->get('validator')->validate(
        $incomingHash,
        $incomingHashConstraint
    );

    if (0 === count($errorList)) {
        // ... this IS a valid hash
    } else {
        // this is *not* a valid hash
        $errorMessage = $errorList[0]->getMessage();

        // ... do something with the error
        throw $this->createNotFoundException('Not a valid hash ID ' . $incomingHash);
    }
在很多控制器中使用它是相当笨拙的。理想情况下,我可以使用自定义验证器作为路线中的一个需求,但这似乎不是一个选项。这些验证器应该是服务吗?理想情况下,我想要像这样的东西

if(!isValid($incomingHash, IncomingHashConstraint)) {
   throw \Exception(); }

关于组织这项活动的最佳方式有什么建议吗?谢谢

有一种非常简单干净的方法

  • 请求负载映射到您的自定义模型类,并根据您的自定义验证类进行验证
  • 如果有错误,那么您将获得错误列表
  • 如果没有错误,那么您可以使用所有 有效负载数据映射到其中的相关属性,然后 将该模型类用于进一步的逻辑(您应该在 服务类不在控制器中)
  • 我会给出一个有效的例子,但如果你想要一个完整的例子。如果你应用它,你会有一个非常非常薄的控制器。字面上不超过15行。这样你就会得到上面@SergioIvanuzzo所说的

    安装jms/序列化程序包

    自定义模型类

    namespace Application\FrontendBundle\Model;
    
    use Application\FrontendBundle\Validator\Constraint as PersonAssert;
    use JMS\Serializer\Annotation as Serializer;
    
    /**
     * @PersonAssert\Person
     */
    class Person
    {
        /**
         * @var int
         * @Serializer\Type("integer")
         */
        public $id;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $name;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $dob;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $whatever;
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    
    /**
     * @Annotation
     */
    class Person extends Constraint
    {
        public function getTargets()
        {
            return self::CLASS_CONSTRAINT;
        }
    
        public function validatedBy()
        {
            return get_class($this).'Validator';
        }
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    use Symfony\Component\Validator\ConstraintValidator;
    
    class PersonValidator extends ConstraintValidator
    {
        public function validate($person, Constraint $constraint)
        {
            if (!is_numeric($person->id)) {
                $this->context->buildViolation('Id must be a numeric value.')->addViolation();
            }
    
            if ($person->name == 'Acyra') {
                $this->context->buildViolation('You name is weird.')->addViolation();
            }
    
            if ($person->dob == '28/11/2014') {
                $this->context->buildViolation('You are too young.')->addViolation();
            }
    
            // I'm not interested in validating $whatever property of Person model!
        }
    }
    
    您的自定义验证程序类

    namespace Application\FrontendBundle\Model;
    
    use Application\FrontendBundle\Validator\Constraint as PersonAssert;
    use JMS\Serializer\Annotation as Serializer;
    
    /**
     * @PersonAssert\Person
     */
    class Person
    {
        /**
         * @var int
         * @Serializer\Type("integer")
         */
        public $id;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $name;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $dob;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $whatever;
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    
    /**
     * @Annotation
     */
    class Person extends Constraint
    {
        public function getTargets()
        {
            return self::CLASS_CONSTRAINT;
        }
    
        public function validatedBy()
        {
            return get_class($this).'Validator';
        }
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    use Symfony\Component\Validator\ConstraintValidator;
    
    class PersonValidator extends ConstraintValidator
    {
        public function validate($person, Constraint $constraint)
        {
            if (!is_numeric($person->id)) {
                $this->context->buildViolation('Id must be a numeric value.')->addViolation();
            }
    
            if ($person->name == 'Acyra') {
                $this->context->buildViolation('You name is weird.')->addViolation();
            }
    
            if ($person->dob == '28/11/2014') {
                $this->context->buildViolation('You are too young.')->addViolation();
            }
    
            // I'm not interested in validating $whatever property of Person model!
        }
    }
    
    控制器

    如果您不将控制器用作服务,那么您可以像上面那样使用
    $this->get('put_the_name_here')
    直接访问
    验证程序
    序列化程序
    服务

    ...
    use JMS\Serializer\SerializerInterface;
    use Symfony\Component\Validator\Validator\ValidatorInterface;
    ....
    
    /**
     * @Route("person", service="application_frontend.controller.bank")
     */
    class PersonController extends Controller
    {
        private $validator;
        private $serializer;
    
        public function __construct(
            ValidatorInterface $validator,
            SerializerInterface $serializer
        ) {
            $this->validator = $validator;
            $this->serializer = $serializer;
        }
    
        /**
         * @param Request $request
         *
         * @Route("/person")
         * @Method({"POST"})
         *
         * @return Response
         */
        public function personAction(Request $request)
        {
            $person = $this->validatePayload(
                $request->getContent(),
                'Application\FrontendBundle\Model\Person'
            );
            if ($person instanceof Response) {
                return $person;
            }
    
            print_r($person);
            // Now you can carry on doing things in your service class
        }
    
        private function validatePayload($payload, $model, $format = 'json')
        {
            $payload = $this->serializer->deserialize($payload, $model, $format);
    
            $errors = $this->validator->validate($payload);
            if (count($errors)) {
                return new Response('Some errors', 400);
            }
    
            return $payload;
        }
    }
    
    示例

    namespace Application\FrontendBundle\Model;
    
    use Application\FrontendBundle\Validator\Constraint as PersonAssert;
    use JMS\Serializer\Annotation as Serializer;
    
    /**
     * @PersonAssert\Person
     */
    class Person
    {
        /**
         * @var int
         * @Serializer\Type("integer")
         */
        public $id;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $name;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $dob;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $whatever;
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    
    /**
     * @Annotation
     */
    class Person extends Constraint
    {
        public function getTargets()
        {
            return self::CLASS_CONSTRAINT;
        }
    
        public function validatedBy()
        {
            return get_class($this).'Validator';
        }
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    use Symfony\Component\Validator\ConstraintValidator;
    
    class PersonValidator extends ConstraintValidator
    {
        public function validate($person, Constraint $constraint)
        {
            if (!is_numeric($person->id)) {
                $this->context->buildViolation('Id must be a numeric value.')->addViolation();
            }
    
            if ($person->name == 'Acyra') {
                $this->context->buildViolation('You name is weird.')->addViolation();
            }
    
            if ($person->dob == '28/11/2014') {
                $this->context->buildViolation('You are too young.')->addViolation();
            }
    
            // I'm not interested in validating $whatever property of Person model!
        }
    }
    
    请求1

    {
      "id": 66,
      "name": "Acyraaaaa",
      "dob": "11/11/1111",
      "whatever": "test"
    }
    
    Application\FrontendBundle\Model\Person Object
    (
        [id] => 66
        [name] => Acyraaaaa
        [dob] => 11/11/1111
        [whatever] => test
    )
    
    响应1

    {
      "id": 66,
      "name": "Acyraaaaa",
      "dob": "11/11/1111",
      "whatever": "test"
    }
    
    Application\FrontendBundle\Model\Person Object
    (
        [id] => 66
        [name] => Acyraaaaa
        [dob] => 11/11/1111
        [whatever] => test
    )
    
    请求2

    {
      "id": "Hello",
      "name": "Acyra",
      "dob": "28/11/2014"
    }
    
    400 Bad request
    Some errors
    
    响应2

    {
      "id": "Hello",
      "name": "Acyra",
      "dob": "28/11/2014"
    }
    
    400 Bad request
    Some errors
    
    如果您转到我在上面给您的链接并应用其余的链接,那么您实际上会收到正确的错误消息,如:

    {
        "errors": {
            "id": "Id must be a numeric value.",
            "name": "You name is weird.",
            "dob": "You are too young."
        }
    }
    

    有一种非常简单和干净的方法

  • 请求负载映射到您的自定义模型类,并根据您的自定义验证类进行验证
  • 如果有错误,那么您将获得错误列表
  • 如果没有错误,那么您可以使用所有 有效负载数据映射到其中的相关属性,然后 将该模型类用于进一步的逻辑(您应该在 服务类不在控制器中)
  • 我会给出一个有效的例子,但如果你想要一个完整的例子。如果你应用它,你会有一个非常非常薄的控制器。字面上不超过15行。这样你就会得到上面@SergioIvanuzzo所说的

    安装jms/序列化程序包

    自定义模型类

    namespace Application\FrontendBundle\Model;
    
    use Application\FrontendBundle\Validator\Constraint as PersonAssert;
    use JMS\Serializer\Annotation as Serializer;
    
    /**
     * @PersonAssert\Person
     */
    class Person
    {
        /**
         * @var int
         * @Serializer\Type("integer")
         */
        public $id;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $name;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $dob;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $whatever;
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    
    /**
     * @Annotation
     */
    class Person extends Constraint
    {
        public function getTargets()
        {
            return self::CLASS_CONSTRAINT;
        }
    
        public function validatedBy()
        {
            return get_class($this).'Validator';
        }
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    use Symfony\Component\Validator\ConstraintValidator;
    
    class PersonValidator extends ConstraintValidator
    {
        public function validate($person, Constraint $constraint)
        {
            if (!is_numeric($person->id)) {
                $this->context->buildViolation('Id must be a numeric value.')->addViolation();
            }
    
            if ($person->name == 'Acyra') {
                $this->context->buildViolation('You name is weird.')->addViolation();
            }
    
            if ($person->dob == '28/11/2014') {
                $this->context->buildViolation('You are too young.')->addViolation();
            }
    
            // I'm not interested in validating $whatever property of Person model!
        }
    }
    
    您的自定义验证程序类

    namespace Application\FrontendBundle\Model;
    
    use Application\FrontendBundle\Validator\Constraint as PersonAssert;
    use JMS\Serializer\Annotation as Serializer;
    
    /**
     * @PersonAssert\Person
     */
    class Person
    {
        /**
         * @var int
         * @Serializer\Type("integer")
         */
        public $id;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $name;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $dob;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $whatever;
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    
    /**
     * @Annotation
     */
    class Person extends Constraint
    {
        public function getTargets()
        {
            return self::CLASS_CONSTRAINT;
        }
    
        public function validatedBy()
        {
            return get_class($this).'Validator';
        }
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    use Symfony\Component\Validator\ConstraintValidator;
    
    class PersonValidator extends ConstraintValidator
    {
        public function validate($person, Constraint $constraint)
        {
            if (!is_numeric($person->id)) {
                $this->context->buildViolation('Id must be a numeric value.')->addViolation();
            }
    
            if ($person->name == 'Acyra') {
                $this->context->buildViolation('You name is weird.')->addViolation();
            }
    
            if ($person->dob == '28/11/2014') {
                $this->context->buildViolation('You are too young.')->addViolation();
            }
    
            // I'm not interested in validating $whatever property of Person model!
        }
    }
    
    控制器

    如果您不将控制器用作服务,那么您可以像上面那样使用
    $this->get('put_the_name_here')
    直接访问
    验证程序
    序列化程序
    服务

    ...
    use JMS\Serializer\SerializerInterface;
    use Symfony\Component\Validator\Validator\ValidatorInterface;
    ....
    
    /**
     * @Route("person", service="application_frontend.controller.bank")
     */
    class PersonController extends Controller
    {
        private $validator;
        private $serializer;
    
        public function __construct(
            ValidatorInterface $validator,
            SerializerInterface $serializer
        ) {
            $this->validator = $validator;
            $this->serializer = $serializer;
        }
    
        /**
         * @param Request $request
         *
         * @Route("/person")
         * @Method({"POST"})
         *
         * @return Response
         */
        public function personAction(Request $request)
        {
            $person = $this->validatePayload(
                $request->getContent(),
                'Application\FrontendBundle\Model\Person'
            );
            if ($person instanceof Response) {
                return $person;
            }
    
            print_r($person);
            // Now you can carry on doing things in your service class
        }
    
        private function validatePayload($payload, $model, $format = 'json')
        {
            $payload = $this->serializer->deserialize($payload, $model, $format);
    
            $errors = $this->validator->validate($payload);
            if (count($errors)) {
                return new Response('Some errors', 400);
            }
    
            return $payload;
        }
    }
    
    示例

    namespace Application\FrontendBundle\Model;
    
    use Application\FrontendBundle\Validator\Constraint as PersonAssert;
    use JMS\Serializer\Annotation as Serializer;
    
    /**
     * @PersonAssert\Person
     */
    class Person
    {
        /**
         * @var int
         * @Serializer\Type("integer")
         */
        public $id;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $name;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $dob;
    
        /**
         * @var string
         * @Serializer\Type("string")
         */
        public $whatever;
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    
    /**
     * @Annotation
     */
    class Person extends Constraint
    {
        public function getTargets()
        {
            return self::CLASS_CONSTRAINT;
        }
    
        public function validatedBy()
        {
            return get_class($this).'Validator';
        }
    }
    
    namespace Application\FrontendBundle\Validator\Constraint;
    
    use Symfony\Component\Validator\Constraint;
    use Symfony\Component\Validator\ConstraintValidator;
    
    class PersonValidator extends ConstraintValidator
    {
        public function validate($person, Constraint $constraint)
        {
            if (!is_numeric($person->id)) {
                $this->context->buildViolation('Id must be a numeric value.')->addViolation();
            }
    
            if ($person->name == 'Acyra') {
                $this->context->buildViolation('You name is weird.')->addViolation();
            }
    
            if ($person->dob == '28/11/2014') {
                $this->context->buildViolation('You are too young.')->addViolation();
            }
    
            // I'm not interested in validating $whatever property of Person model!
        }
    }
    
    请求1

    {
      "id": 66,
      "name": "Acyraaaaa",
      "dob": "11/11/1111",
      "whatever": "test"
    }
    
    Application\FrontendBundle\Model\Person Object
    (
        [id] => 66
        [name] => Acyraaaaa
        [dob] => 11/11/1111
        [whatever] => test
    )
    
    响应1

    {
      "id": 66,
      "name": "Acyraaaaa",
      "dob": "11/11/1111",
      "whatever": "test"
    }
    
    Application\FrontendBundle\Model\Person Object
    (
        [id] => 66
        [name] => Acyraaaaa
        [dob] => 11/11/1111
        [whatever] => test
    )
    
    请求2

    {
      "id": "Hello",
      "name": "Acyra",
      "dob": "28/11/2014"
    }
    
    400 Bad request
    Some errors
    
    响应2

    {
      "id": "Hello",
      "name": "Acyra",
      "dob": "28/11/2014"
    }
    
    400 Bad request
    Some errors
    
    如果您转到我在上面给您的链接并应用其余的链接,那么您实际上会收到正确的错误消息,如:

    {
        "errors": {
            "id": "Id must be a numeric value.",
            "name": "You name is weird.",
            "dob": "You are too young."
        }
    }
    

    如果你不想在很多控制器中重复这些逻辑,那么使用事件监听器怎么样?例如,而不是检查多个控制器实例,您可以只向这些控制器添加一个简单的接口并应用您的逻辑。您可以使用所需的逻辑定义一个控制器,然后从中扩展控制器。请看下面的示例,尤其是其中的完整示例链接,它完全符合@SergioIvanuzzo上面所说的,我同意他的观点!如果你不想在很多控制器中重复这些逻辑,那么使用事件监听器怎么样?例如,而不是检查多个控制器实例,您可以只向这些控制器添加一个简单的接口并应用您的逻辑。您可以使用所需的逻辑定义一个控制器,然后从中扩展控制器。请看下面的示例,尤其是其中的完整示例链接,它完全符合@SergioIvanuzzo上面所说的,我同意他的观点!