f:validateWholeBean 在 JSF 2.3 中

f:validateWholeBean in JSF 2.3

我想用 JSF 2.3 实现 f:validateWholeBean。 我试图用 Mojarra 2.3.0-m05 和 Tomcat 8:

来实现这个例子
<h:form>
    <h:panelGroup>
        <h:inputSecret id="passwd" value="#{bean.dataList['passwd']}">
            <f:ajax event="blur" render="passwdvalidator" />
        </h:inputSecret>
        <h:message id="passwdvalidator" for="passwd" />
    </h:panelGroup>

    <h:panelGroup>Confirm Password</h:panelGroup>
    <h:panelGroup>
        <h:inputSecret id="confurmpasswd" value="#{bean.dataList['passwd']}">
            <f:ajax event="blur" render="confurmpasswdvalidator" />
        </h:inputSecret>
        <h:message id="confurmpasswdvalidator" for="confurmpasswd" />
    </h:panelGroup>
    <h:commandButton action="#{bean.submit}">
        <f:ajax render="@form" execute="@form"></f:ajax>
    </h:commandButton>                          

    <f:validateWholeBean  value="#{contactBean}" validationGroups="validateBean.ContactGroup" />
</h:form>

自定义验证器

@Named
@ViewScoped
public class NewAccountValidator implements Validator, Serializable
{
    @Override
    public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException
    {
        // not used
    }

    public void validatePasswords(FacesContext context, UIComponent component, Object value)
    {
        String l;
        String s = value.toString().trim();

        if (s != null)
        {
            // compare passwords
        }
        else
        {
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_INFO,
                s.isEmpty() ? "  This field cannot be empty!" : "  '" + s + "' is not a number!", null));
        }
    }
}

使用 f:validateWholeBean 和自定义 JSF 验证器实现解决方案的正确方法是什么?

您不应该实现“标准”验证器,而应该实现 ConstraintValidator

您可以在 Arjan Tijms Weblog 上找到示例:

<h:form>
    <h:inputText value="#{indexBean.foo}">
        <f:validateBean validationGroups="javax.validation.groups.Default,java.util.RandomAccess"/>
    </h:inputText>
    <h:inputText value="#{indexBean.bar}">
        <f:validateBean validationGroups="javax.validation.groups.Default,java.util.RandomAccess"/>
    </h:inputText>
 
    <f:validateWholeBean value="#{indexBean}" validationGroups="java.util.RandomAccess"/>
 
    <h:commandButton value="submit"/>
</h:form>

带有支持 bean:

@Named
@RequestScoped
@ValidIndexBean(groups = java.util.RandomAccess.class)
public class IndexBean implements ConstraintValidator<ValidIndexBean, IndexBean> {
 
    @Constraint(validatedBy = IndexBean.class)
    @Documented
    @Target(TYPE)
    @Retention(RUNTIME)
    public @interface ValidIndexBean {
        String message() default "Invalid Bean";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    
    @Inject // @EJB
    private PersistenceService service;

    @NotNull
    private String foo;
 
    @NotNull
    private String bar;
 
    @Override
    public void initialize(ValidIndexBean constraintAnnotation) {
        //
    }
 
    @Override
    public boolean isValid(IndexBean other, ConstraintValidatorContext context) {
        // return other.getFoo().equals(other.getBar());

        return service.query("select count(p) from Person p where p.foo like ?1 and p.bar like ?2", other.getFoo(), other.getBar()) == 0;
    }

    ...
}

评论回答:

  • 这是一个普通的 bean,所以是的,它可以是 @ViewScoped。
  • 那么您应该创建多个验证器:让单个验证器执行多个逻辑是一种不好的做法。

无关:

正如我从您发布的代码中看到的那样,您误解了“经典”验证器的使用,使其成为 ManagedBean(CDI 风格),但这是 不是 “普通”使用 JSF Validators/Converters.

我想你没有使用验证器,而是使用验证方法

“经典”验证器应如下所示(参见 here):

@FacesValidator("usernameValidator")
public class UsernameValidator implements Validator, Serializable
{
    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
    {
        // you should use THIS method to validate a single Component's Value
        
        if(query("select count(*) from user where username = '?'", String.valueOf(value)) > 0)
        {
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "invalid username"));
        }
    }
}

应该像这样使用:

<h:inputText value="#{someBean.username}" validator="usernameValidator" />

所以:

  1. “经典”人脸验证器很难验证 一个 组件的值
  2. 他们不应该是@ManagedBean或@Named
  3. 它们应该通过名称引用(validator="usernameValidator" 不使用 EL 表达式 validator="#{usernameValidator}"

但是,Validators/Converters 的最佳做法是“专业化”:他们应该执行单一验证逻辑。

如果您需要验证组件值,即日期,它必须是非空 并且 大于 01/01/1970,您将需要 两个个专门的验证器。