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

问题描述 投票:0回答:1

我想使用一些 Java bean 方法在我的一些输入组件中执行验证,例如

<h:inputText>
。我应该使用
<f:validator>
还是
<f:validateBean>
来实现此目的?我在哪里可以阅读更多相关内容?

validation jsf
1个回答
38
投票

标准方法是实现

Validator
接口。

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

    @Override
    public void validate(FacesContext context, UIComponent component, String value) throws ValidatorException {
        if (value == null || value.isEmpty()) {
            // Let required="true" handle it.
        }

        // ...

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

component
参数表示验证器所附加的组件,通常是
UIInput
的实例。
value
参数表示要验证的值,通常是关联的
UIInput
组件的提交和转换后的值。

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

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

每当验证器抛出

ValidatorException
时,其消息将显示在与输入字段关联的
<h:message>
中。

请注意,检查 null/空白值应委托给

required="true"
属性,如下所示:

<h:inputText id="foo" value="#{bean.foo}" validator="fooValidator"
    required="true" requiredMessage="Please fill out foo" />
<h:message for="foo" />

您还可以在任何

validator
/
<h:inputXxx>
组件的
<h:selectXxx>
属性中使用 EL,其中您引用与
Validator#validate()
具有完全相同的方法签名(相同的方法参数)的托管 bean 方法。 IE。按此顺序采用
FacesContext
UIComponent
Object
参数。

<h:inputText id="foo" value="#{bean.foo}" validator="#{bean.validateFoo}" />
<h:message for="foo" />
public void validateFoo(FacesContext context, UIComponent component, String value) throws ValidatorException {
    if (value == null || value.isEmpty()) {
        // Let required="true" handle it.
    }

    // ...

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

仅当验证器需要访问同一托管 bean 中存在的另一个属性时,这才有用。如果不需要,那么这种方法被认为是紧耦合(因此不好的做法),您应该将验证器拆分到它自己的实现

Validator
接口的类中。

如果您希望基于多个输入值和/或 bean 属性进行验证,最好将它们作为组件的

<f:attribute>
传递。更多详细信息可以在JSF不支持跨字段验证,有解决方法吗?

的答案中找到

您还可以使用

<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<String> {

    @Override
    public void validate(FacesContext context, UIComponent component, String value) throws ValidatorException {
        if (value == null || value.isEmpty()) {
            // Let required="true" handle it.
        }

        // ...

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

请注意,因此使用

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

或者通过这种方式,可以轻松地以 lambda 形式提供:

<h:inputText id="foo" value="#{bean.foo}">
    <f:validator binding="#{bean.fooValidator}" />
</h:inputText>
<h:message for="foo" />
public Validator<String> getFooValidator() {
    return (context, component, value) -> {
        if (value == null || value.isEmpty()) {
            // Let required="true" handle it.
        }

        // ...

        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">
中。

© www.soinside.com 2019 - 2024. All rights reserved.