如何在JSF中进行验证,如何创建自定义验证器在JSF中

16

我想在一些输入组件中(例如<h:inputText>)使用Java bean方法进行验证。我应该使用<f:validator>还是<f:validateBean>?在哪里可以阅读更多相关资料?

1个回答

37

实现 Validator 接口是标准的方法。

@FacesValidator("fooValidator")
public class FooValidator implements Validator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        // ...

        if (valueIsInvalid) {
            throw new ValidatorException(new FacesMessage("Value is invalid!"));
        }
    }

}

@FacesValidator 会将它注册到 JSF 中,使用验证器 ID myValidator,以便您可以在任何 <h:inputXxx>/<h:selectXxx> 组件的 validator 属性中引用它,如下所示:

<h:inputText id="foo" value="#{bean.foo}" validator="fooValidator" />
<h:message for="foo" />

每当验证器抛出ValidatorException时,其消息将显示在与输入字段关联的<h:message>中。

您还可以在任何<h:inputXxx>/<h:selectXxx>组件的validator属性中使用EL,其中引用了具有完全相同方法签名(相同方法参数)的托管bean方法Validator#validate()。即以这个顺序接受FacesContextUIComponentObject参数。

<h:inputText id="foo" value="#{bean.foo}" validator="#{bean.validateFoo}" />
<h:message for="foo" />
public void validateFoo(FacesContext context, UIComponent component, Object value) throws ValidatorException {
    // ...

    if (valueIsInvalid) {
        throw new ValidatorException(new FacesMessage("Value is invalid!"));
    }
}

只有在验证器需要访问同一托管bean中存在的另一个属性时,此方法才有用。如果不需要,则认为这种方法是紧密耦合的(因此是不良实践),您应该将验证器拆分到其自己的类中实现Validator接口。

您还可以使用<f:validator>标签处理程序,如果要在同一组件上附加多个验证器,则这是唯一的方法:

<h:inputText id="foo" value="#{bean.foo}">
    <f:validator validatorId="fooValidator" />
</h:inputText>
<h:message for="foo" />

这将执行上面显示的@FacesValidator("fooValidator")

您还可以使用<f:validator binding>来引用EL范围中某个具体验证器实例,该实例可以通过以下方式指定和提供:

<h:inputText id="foo" value="#{bean.foo}">
    <f:validator binding="#{fooValidator}" />
</h:inputText>
<h:message for="foo" />
@Named("fooValidator")
public class FooValidator implements Validator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        // ...

        if (valueIsInvalid) {
            throw new ValidatorException(new FacesMessage("Value is invalid!"));
        }
    }

}
请注意,这里使用的是@Named而不是@FacesValidator。旧的@ManagedBean在这里也被支持,代替@Named。历史上,这是一个技巧,为了能够在验证器中使用@EJB@Inject。另请参见如何在@FacesValidator中使用@EJB、@PersistenceContext、@Inject、@Autowired注入

或者可以采用这种方式,作为一个lambda简单地提供:

<h:inputText id="foo" value="#{bean.foo}">
    <f:validator binding="#{bean.fooValidator}" />
</h:inputText>
<h:message for="foo" />
public Validator getFooValidator() {
    return (context, component, value) -> {
        // ...

        if (valueIsInvalid) {
            throw new ValidatorException(new FacesMessage("Value is invalid!"));
        }
    };
}

同样的紧耦合问题也适用于此验证器不需要从同一bean获取任何其他属性的情况。

为了更进一步,您可以使用JSR303 bean验证。这将基于注释验证字段。因此,您可以只使用

@Foo
private String foo;

在XHTML方面,无需显式注册任何验证器。如果使用JPA进行持久性操作,默认情况下,在向数据库插入/更新期间也会执行此验证器。由于这将是整个故事,因此这里只是一些入门链接:

还有一个<f:validateBean>标签,但仅在您打算禁用 JSR303 bean 验证时才有用。然后,将输入组件(甚至整个表单)放置在 <f:validateBean disabled="true"> 中。

另请参阅:


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