3.2.13.2. 运行时验证

在 UI 中验证

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

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

  1. @UiController("sample_NewScreen")
  2. @UiDescriptor("new-screen.xml")
  3. public class NewScreen extends Screen {
  4. @Inject
  5. private TextField<String> field1;
  6. @Inject
  7. private TextField<String> field2;
  8. @Subscribe
  9. protected void onInit(InitEvent event) {
  10. field1.getValidators().stream()
  11. .filter(BeanPropertyValidator.class::isInstance)
  12. .forEach(field1::removeValidator); (1)
  13. field2.getValidators().stream()
  14. .filter(BeanPropertyValidator.class::isInstance)
  15. .forEach(validator -> {
  16. ((BeanPropertyValidator) validator).setValidationGroups(new Class[] {UiComponentChecks.class}); (2)
  17. });
  18. }
  19. }
1从 UI 组件中完全删除 bean 验证。
2这里,验证器将仅检查显式设置了 UiComponentChecks 组的约束,因为没有传递默认组。

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

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

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

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

  1. <window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
  2. caption="msg://editorCaption"
  3. class="com.company.demo.web.task.TaskEdit"
  4. datasource="taskDs"
  5. crossFieldValidate="false">
  6. <!-- ... -->
  7. </window>
  1. public class TaskEdit extends StandardEditor<Task> {
  2. @Subscribe
  3. protected void onInit(InitEvent event) {
  4. setCrossFieldValidate(false);
  5. }
  6. }

DataManager中的验证

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

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

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

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

中间件服务验证

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

  1. public interface TaskService {
  2. String NAME = "demo_TaskService";
  3. @Validated
  4. @NotNull
  5. String completeTask(@Size(min = 5) String comment, @NotNull Task task);
  6. }

@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. [
    2. {
    3. "message": "Invalid email: aaa",
    4. "messageTemplate": "{msg://com.company.demo.entity/Customer.email.validationMsg}",
    5. "path": "email",
    6. "invalidValue": "aaa"
    7. }
    8. ]
    • path - 表示被验证对象的无效属性在对象关系图中的路径。

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

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

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

以编程的方式进行验证

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

  1. @Inject
  2. private BeanValidation beanValidation;
  3. public void save(Foo foo) {
  4. Validator validator = beanValidation.getValidator();
  5. Set<ConstraintViolation<Foo>> violations = validator.validate(foo);
  6. // ...
  7. }