Symfony表单验证 vs 验证器服务

12

我遇到了一个奇怪的问题,感觉自己漏了什么。

我正在使用Symfony 2.7,并且正在开发一个用户密码更新功能。

我有一个用户实体(扩展了FosUserBundle用户实体),具有多个属性。其中之一是plainPassword(当然不会被持久化到数据库中)。

User.php

...

/**
 * @ORM\Table(name="prefix_users")
 * @ORM\Entity(repositoryClass="UserRepository")
 *
 * @UniqueEntity(fields={"email"}, message="unique", groups={"registration"})
 * @UniqueEntity("username", message="unique", groups={"registration"})
 *
 * @ExclusionPolicy("all")
 */
class User extends BaseUser
{

...

    /* @var string
     *
     * @Assert\NotBlank(message="blank",groups={"accountUpdate", "passwordUpdate"})
     * @Assert\Length(min=8, minMessage="short", max=4096, maxMessage="long")
     */
    protected $plainPassword;

正如您所见,我正在使用注释来验证我的属性。我还在使用验证组来确保只有正确的属性被验证。

我创建了一个表单

UserUpdatePasswordType.php

class UserUpdatePasswordType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('plainPassword', 'repeated', array(
            'type' => 'password',
            'invalid_message' => 'password_mismatch',
        ));
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'My\Bundle\UserBundle\Entity\User',
            'csrf_protection' => false,
            'intention' => 'resetting',
            /**
             * Need to add method to prevent this form from bein marked as invalid.
             * @see http://sroze.io/2013/10/30/symfony2-form-patch-requests-and-handlerequest-form-never-valid/
             */
            'method' => 'PATCH'
        ));
    }

我正在使用FOSRestBundle构建RESTful API,如果表单验证失败,我想提供一个漂亮且清晰的输出。

据我所知,有两种方式可以验证表单:

  1. $formErrors = $this->get('validator')->validate($user, null, ['passwordUpdate']);
  2. $form->isValid() 并使用$form->getErrors()获取错误

现在出现了奇怪的情况,上述两种方法给出不同的验证错误。

请求参数

user_update_password[plainPassword][first]:sffsdfdsfds
user_update_password[plainPassword][second]:fdsfdsfsd

从$formErrors获取的响应为 $this->get('validator')->validate($user, null, ['passwordUpdate']); 响应不正确,因为 plainPassword 不为空。

{
      "property_path": "plainPassword",
      "message": "blank"
}

响应表单$form->isValid()看起来更好

"form": {
      "children": {
        "plainPassword": {
          "children": {
            "first": {
              "errors": [
                "password_mismatch"
              ]
            },
            "second": {}
          }
        }
      }
    },
    "errors": []

我唯一能看出来的区别在于我没有在$form->isValid()中提供验证组名称。

为什么我会得到不同的结果,我应该怎么做才能解决问题?我可以在$form->isValid()中提供一个验证组名称吗?还是应该修复$validator->validate代码中的问题?

我很想看看其他基于Symfony2的API是如何管理这个问题的...


我可以问一下,为什么你要在第一时间重复使用FOS用户包的功能呢?BaseUser中有一个plainPassword字段,并且验证约束非常容易编辑。 - Dmitry Malyshenko
@DmitryMalyshenko,谢谢你的提问,但我使用这个作为我所面临问题的例子。在我的应用程序中,我没有重复任何FOSUser代码。 - ivodvb
3个回答

7

导致差异的原因有三个。

  1. 可能是在通过服务进行验证时,您没有将数据从request设置到model。使用$form->submit($request)作为安全代理数据设置器。
  2. 您没有在表单中使用验证组(请在setDefaults中执行此操作)。顺便说一下,您的@Assert\Length不会被使用,除非您向该注释添加组或者向service/form验证器数组添加验证组Default
  3. 您在表单中使用了repeated类型,它与您的模型Constraints相比具有额外的Constraints。表单验证器检查传递验证组(默认情况下使用“Default”组)的模型和表单约束。

2
实际上,除了使用验证组之外,还有另一个区别。当您使用表单(而不仅仅是验证器)时,涉及到'重复'类型。
引用: 验证 重复字段的关键特性之一是内部验证(您无需进行任何设置即可完成),它强制两个字段具有匹配的值。如果两个字段不匹配,则会向用户显示错误。
因此,在某种情况下,您正在使用额外的约束条件,在另一种情况下则没有。验证给出不同的结果也就不足为奇了。
因此,在您的情况下,我可能会为User对象创建自己的“repeat”实现,并使用验证器服务来验证密码。
我看到的另一个选项是使用表单对象来验证User,但这不是一个清晰明确的API方式。

为什么在 API 中不以干净的方式使用表单?API 只是具有不同响应的请求,而不是 HTML。你可以在 JSON 或 XML 中返回表单错误。此外,没有什么好理由放弃表单转换器和订阅者,仅因为你构建了 API。 - Maciej Pyszyński
因为Symfony文档中关于表单的章节从“处理HTML表单是最常见的...”开始。这个组件的核心是为HTML设计的,我认为在不使用HTML时使用它是不正确的。 - Dmitry Malyshenko
1
不,它不是。只有 $form->createView() 部分与 HTML 相关,而且它并非必须的。直接使用请求中的数据存在很大的安全问题,表单规范化数据并防止大多数安全问题。 - Maciej Pyszyński

-2

两者的主要区别在于一个验证器验证模型,另一个验证表单数据。模型要求字段不能为空。FOSUser bundle ChangePasswordFormTypeUserPassword constraint添加到表单中进行验证。UserPassword验证器检查表单值是否被正确提交。

如果您在API中使用www-form-urlencoded数据,请使用表单验证器。它会提供更精确的错误信息,因为它们与提交的数据状态相关,而不是在将其转换为模型后的数据状态。


同时验证模型和表单还提供额外的约束。 - Maciej Pyszyński

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接