3.2.13.2. 运行时验证
在 UI 中验证

连接到数据源的通用 UI 组件获取 BeanValidator 实例来检查字段的值。验证器是从可视化组件实现的 Component.Validatable.validate() 方法调用的。如果验证不通过,会抛出 CompositeValidationException 异常,这个异常实例中包含了一组违规信息的集合。

可以移除标准的验证器,也可以使用不同的约束组初始化标准验证器:

@UiController("sample_NewScreen")
@UiDescriptor("new-screen.xml")
public class NewScreen extends Screen {

    @Inject
    private TextField<String> field1;
    @Inject
    private TextField<String> field2;

    @Subscribe
    protected void onInit(InitEvent event) {
        field1.getValidators().stream()
                .filter(BeanPropertyValidator.class::isInstance)
                .forEach(field1::removeValidator); (1)

        field2.getValidators().stream()
                .filter(BeanPropertyValidator.class::isInstance)
                .forEach(validator -> {
                    ((BeanPropertyValidator) validator).setValidationGroups(new Class[] {UiComponentChecks.class}); (2)
                });
    }
}
1 从 UI 组件中完全删除 bean 验证。
2 这里,验证器将仅检查显式设置了 UiComponentChecks 组的约束,因为没有传递默认组。

默认情况下,BeanValidator 具有 DefaultUiComponentChecks 分组。

如果实体属性带有 @NotNull 注解且没有定义约束组,则在元数据中这个属性会被标记为强制的(mandatory),并且通过数据源使用此属性的 UI 组件将具有 required = true 属性。

DateFieldDatePicker组件使用 @Past@PastOrPresent@Future@FutureOrPresent 注解自动设置其 rangeStartrangeEnd 属性,不过这里忽略了时间部分。

如果约束包含 UiCrossFieldChecks 组并且所有属性级别的检查都通过了,编辑界面将在提交时做类级别约束的验证。可以在 XML 描述或界面控制器使用 crossFieldValidate 属性关闭此验证:

<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://editorCaption"
        class="com.company.demo.web.task.TaskEdit"
        datasource="taskDs"
        crossFieldValidate="false">
    <!-- ... -->
</window>
public class TaskEdit extends StandardEditor<Task> {
    @Subscribe
    protected void onInit(InitEvent event) {
        setCrossFieldValidate(false);
    }
}
DataManager中的验证

DataManager 可以对保存的实体实例进行验证。以下参数会影响验证:

  • cuba.dataManagerBeanValidation 应用程序属性设置设置是否进行验证的全局默认值。

  • 也可以覆盖上面的全局默认值,在 UI 界面保存数据的时候,可以在使用 DataManager.commit() 或者 DataContext.PreCommitEvent 里为 CommitContext 设置 CommitContext.ValidationMode

  • 也可以提供一个 验证组 的列表给 CommitContext 或者 DataContext.PreCommitEvent,这样可以只应用定义的约束的一部分。

中间件服务验证

如果服务接口中的方法带有 @Validated 注解,则中间件服务会对方法的参数和返回结果执行验证。例如:

public interface TaskService {
    String NAME = "demo_TaskService";

    @Validated
    @NotNull
    String completeTask(@Size(min = 5) String comment, @NotNull Task task);
}

@Validated 注解可以指定约束组以使验证应用到某组约束上,如果没有指定任何组,默认使用以下约束组:

  • DefaultServiceParametersChecks - 进行方法参数验证时

  • DefaultServiceResultChecks - 进行方法返回值验证时

在验证错误时会抛出 MethodParametersValidationExceptionMethodResultValidationException 异常。

如果要在服务中以编程的方式执行某些自定义验证,请使用 CustomValidationException 来通知客户端有关验证的错误信息,这样可以与标准 bean 验证错误信息保持相同的格式。此异常也可以跟 REST API 客户端有特定的关联。

在 REST API 中验证

对于创建和更新操作, 通常 REST API 会自动执行 bean 验证。验证错误会以如下方式返回给客户端:

  • MethodResultValidationExceptionValidationException 导致 500 Server error HTTP 状态

  • MethodParametersValidationExceptionConstraintViolationExceptionCustomValidationException 导致 400 Bad request HTTP 状态

  • 格式为 Content-Type: application/json 的响应体将包含一个对象列表,每个对象都包含属性 messagemessageTemplatepathinvalidValue 属,例如:

    [
        {
            "message": "Invalid email: aaa",
            "messageTemplate": "{msg://com.company.demo.entity/Customer.email.validationMsg}",
            "path": "email",
            "invalidValue": "aaa"
        }
    ]
    • path - 表示被验证对象的无效属性在对象关系图中的路径。

    • messageTemplate - 消息模板字符串,这个模板字符串是在 message 注解属性中定义。

    • message - 包含验证消息的实际值 。

    • invalidValue - 属性值类型是 StringDateNumberEnumUUID 中的其中之一时才返回。

以编程的方式进行验证

可以使用 BeanValidation 基础设施接口以编程的方式执行验证。该接口可在中间件和客户端层使用。它用于获取执行验证的 javax.validation.Validator 实现。验证的结果是一组 ConstraintViolation 对象。例如:

@Inject
private BeanValidation beanValidation;

public void save(Foo foo) {
    Validator validator = beanValidation.getValidator();
    Set<ConstraintViolation<Foo>> violations = validator.validate(foo);
    // ...
}