【问题标题】:How to apply JSF Validator after annotations constraints?如何在注释约束后应用 JSF Validator?
【发布时间】:2013-11-17 02:34:34
【问题描述】:

我的问题更多是关于性能而不是功能。 我正在处理注册表单的电子邮件字段。 为了验证,我在User 实体中为电子邮件格式 使用注释,如果此电子邮件已使用,我使用EmailExistValidator 来检查数据库。

@Entity
@Table(name = "Users")
public class User {

    @Column(name = "email")
    @NotNull(message = "Email address required")
    @Pattern(regexp = "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)", message = "Invalid email address")
    private String email;

    // ... (other fields and getters/setters here)
}

验证者:

@FacesValidator(value = "emailExistValidator")
public class EmailExistValidator implements Validator {

    private static final String EMAIL_ALREADY_EXISTS = "This email address is already used.";

    @EJB
    private UserDao userDao;

    @Override
    public void validate(FacesContext context, UIComponent component, Object value)
            throws ValidatorException {
        String email = (String) value;
        try {
            if (email != null && userDao.findByEmail(email) != null) {
                throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        EMAIL_ALREADY_EXISTS, null));
            }
        } catch (DaoException e) {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(),
                    null);
            FacesContext facesContext = FacesContext.getCurrentInstance();
            facesContext.addMessage(component.getClientId(facesContext), message);
        }
    }
}

根据我的测试,如果一封电子邮件没有验证正则表达式模式,验证器仍然适用。 我不喜欢执行数据库查询来检查电子邮件是否存在的想法,而我已经知道这封电子邮件甚至无效。它确实会降低性能(在模糊上使用 ajax 时更是如此表单中的事件)。

我有一些猜测,但我找不到明确的答案,所以我在这里问几个问题:

  1. 将验证器与注释约束混合在一起是一件坏事吗?

  2. 在验证期间,是否会检查每个约束,即使其中一个没有通过? (如果是,那么是所有消息(针对每个约束)都添加到组件的FacesContext,还是只是第一条消息?)

  3. 如果问题 2 为“是”,是否有任何方法可以在未验证一个约束时强制停止验证?

  4. 如果问题 2 为“否”或问题 3 为“是”,是否在某处指定了约束/验证器的应用顺序?这个订单可以定制吗?

如果这很重要,我将 PrimeFaces 用于我的 Facelets 组件。

我知道我可以将所有内容放入验证器并随时停止,但此验证器仅用于注册表单。当用于登录时,我只会检查实体的注释约束,而不是“已经存在”部分。此外,在我看来,注释和单约束验证器比多约束验证器的内容更具可读性。

【问题讨论】:

    标签: validation jsf primefaces bean-validation


    【解决方案1】:

    JSF 验证在 JSR303 bean 验证之前按设计运行。因此,当 bean 验证失败时,技术上没有办法跳过 JSF 验证。如果它们反过来运行,那么理论上您只需在 JSF 验证器中检查 UIInput#isValid()

    UIInput input = (UIInput) component;
    
    if (!input.isValid()) {
        return;
    }
    

    很遗憾,没有提供 API 的方法来控制 JSF-JSR303 验证顺序。您最好的选择是将 JSF 验证器转换为真正的 JSR303 bean 验证器,并将其分配给您声明在默认组之后运行 的不同组。在 JSR303 中,约束是在每个组的基础上进行验证的。如果一个验证组失败,则不会执行任何后续验证组。

    第一create a custom JSR303 bean validation constraint annotation

    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = UniqueEmailValidator.class)
    @Documented
    public @interface UniqueEmail {
        String message() default "{invalid.unique.email}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    然后create a custom JSR303 bean validation constraint validator:

    public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
    
        @Override
        public void initialize(UniqueEmail annotation) {
            // Grab EJB here via JNDI?
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            return userDao.findByEmail(value) == null;
        }
    
    }
    

    不幸的是,ConstraintValidator 中的 @EJB/@Inject 在 Java EE 6 / JSR303 Bean Validation 1.0 中不受本机支持。它仅在 Java EE 7 / JSR303 Bean Validation 1.1 中受支持。然而,它们可以通过 JNDI 获得。

    然后create a custom validation group:

    public interface ExpensiveChecks {}
    

    最后在您的实体上使用它,通过@GroupSequence 注释声明组排序(默认组由当前类标识):

    @Entity
    @Table(name = "Users")
    @GroupSequence({ User.class, ExpensiveChecks.class })
    public class User {
    
        @Column(name = "email")
        @NotNull(message = "Email address required")
        @Pattern(regexp = "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)", message = "Invalid email address")
        @UniqueEmail(groups = ExpensiveChecks.class, message = "This email address is already used")
        private String email;
    
        // ...
    }
    

    【讨论】:

      猜你喜欢
      • 2011-11-21
      • 1970-01-01
      • 2013-03-03
      • 1970-01-01
      • 2018-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-15
      相关资源
      最近更新 更多