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




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



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





  1. @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@Future 注解自动设置其 rangeStartrangeEnd 属性,不过这里忽略了时间部分。



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





  1. <window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
    caption="msg://editorCaption"
    class="com.company.demo.web.task.TaskEdit"
    datasource="taskDs"
    crossFieldValidate="false">
    <!— … —>
    </window>








  1. public class TaskEdit extends StandardEditor<Task> {
    @Subscribe
    protected void onInit(InitEvent event) {
    setCrossFieldValidate(false);
    }
    }






中间件服务验证




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





  1. 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 属,例如:




  1. [
    {
    "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 对象。例如:





  1. @Inject
    private BeanValidation beanValidation;

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