Validation

本指南涵盖如何使用 Hibernate Validator/Bean Validation:

  • 验证您的 REST 接口的输入/输出;

  • 验证您的业务服务方法的参数和返回值。

必备条件

要完成本指南,您需要:

  • 15 分钟以内

  • IDE

  • 安装了JDK 1.8+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.6.2+

结构

本指南中创建的应用程序非常简单。 用户在网页上填写表格。 网页将表单内容以 JSON 格式 (使用 Ajax) 发送到 BookResource. BookResource 验证用户输入并返回 JSON 格式 结果

Architecture

成果

我们建议您按照下面章节指示,一步一步创建应用程序。 但是你也可以直接跳到已完成的例子。

克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git, 或下载 压缩包

代码在 validation-quickstart 目录

创建 Maven 项目

首先,我们需要一个新项目。 通过以下命令创建一个新项目:

mvn io.quarkus:quarkus-maven-plugin:1.3.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=validation-quickstart \
    -DclassName="org.acme.validation.BookResource" \
    -Dpath="/books" \
    -Dextensions="resteasy-jsonb, hibernate-validator"
cd validation-quickstart

此命令生成一个 Maven 项目结构并导入 RESTEasy/JAX-RS, JSON-B 和 Hibernate Validator/Bean Validation extensions。

访问 Validator

编辑 org.acme.validation.BookResource 类,注入 Validator 对象如下:

@Inject
Validator validator;

Validator 允许检查对特定对象的约束。

约束

在本应用程序中,我们将测试一个基本对象,但我们支持复杂约束,并且可以验证对象图。 创建 org.acme.validation.Book 类,内容如下:

package org.acme.validation;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Min;

public class Book {

    @NotBlank(message="Title may not be blank")
    public String title;

    @NotBlank(message="Author may not be blank")
    public String author;

    @Min(message="Author has been very lazy", value=1)
    public double pages;
}

约束添加到字段中,当对象被验证时将检查字段值。 getter 和 setter方法也用于JSON映射。

JSON 映射和验证

回到 BookResource 类。 增加以下方法:

@Path("/manual-validation")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Result tryMeManualValidation(Book book) {
    Set<ConstraintViolation<Book>> violations = validator.validate(book);
    if (violations.isEmpty()) {
        return new Result("Book is valid! It was validated by manual validation.");
    } else {
        return new Result(violations);
    }
}

它现在还编译不过, 缺少 Result ,但我们过会就添加它。

方法参数 (book) 是自动从 JSON 创建的。

该方法使用 Validator 来检查 payload。 它会返回违背约束的集合。 如果此 set 为空,意味着对象是有效的。 如果出现错误,消息会被拼接起来发回浏览器。

现在我们创建 Result 内部类:

public static class Result {

    Result(String message) {
        this.success = true;
        this.message = message;
    }

    Result(Set<? extends ConstraintViolation<?>> violations) {
        this.success = false;
        this.message = violations.stream()
             .map(cv -> cv.getMessage())
             .collect(Collectors.joining(", "));
    }

    private String message;
    private boolean success;

    public String getMessage() {
        return message;
    }

    public boolean isSuccess() {
        return success;
    }

}

该类非常简单,仅包含 2 个字段和相关的 getters 和 setters。 因为我们指明会产生 JSON ,所以会自动映射成 JSON 。

REST 接口验证

当手动使用 Validator 时,可能对某些高级用途有用, 如果您只想验证参数或返回值或您的 REST 接口, 你可以直接给它注入约束 (@NotNull, @Digits…​) 或使用 @Valid (它将把验证传输到 bean 中)。

让我们创建一个验证请求中提供的 Book 的终点:

@Path("/end-point-method-validation")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Result tryMeEndPointMethodValidation(@Valid Book book) {
    return new Result("Book is valid! It was validated by end point method validation.");
}

正如你可以看到的那样,我们不必再手动验证所提供的 Book ,因为它是自动验证的。

If a validation error is triggered, a violation report is generated and serialized as JSON as our end point produces a JSON output. It can be extracted and manipulated to display a proper error message.

Service 方法验证

在接口级别声明验证规则可能并非易事,因为它可能重复某些业务验证。

最好的选项是用您的约束注解您的业务 service 方法(或我们这的 @Valid):

package org.acme.validation;

import javax.enterprise.context.ApplicationScoped;
import javax.validation.Valid;

@ApplicationScoped
public class BookService {

    public void validateBook(@Valid Book book) {
        // your business logic here
    }
}

在 rest 接口调用 service 方法会自动验证 Book

@Inject BookService bookService;

@Path("/service-method-validation")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Result tryMeServiceMethodValidation(Book book) {
    try {
        bookService.validateBook(book);
        return new Result("Book is valid! It was validated by service method validation.");
    } catch (ConstraintViolationException e) {
        return new Result(e.getConstraintViolations());
    }
}

注意,如果您想要将验证错误推送到前端, 您必须 catch 异常并推送您自己的信息 ,因为他们不会自动被推送到 JSON 输出。

请记住,您通常不想向公众透露您服务的内部信息 - 尤其不想透露违背约束对象中的有效值。

前端

现在让我们添加简单的网页来与我们的 BookResource 交互。 Quarkus 自动为 META-INF/resources 目录中包含的静态资源服务。 用 index.html 文件替换 src/main/resources/META-INF/resources 目录中的 index.html 文件。

运行应用程序

现在,让我们看看应用。 运行它:

./mvnw compile quarkus:dev

然后打开您的浏览器访问 http://localhost:8080/ :

  1. 输入 book 详细信息(有效或无效)

  2. 点击 Try me…​ 按钮来检查您的数据是否有效,使用我们上面介绍的方法之一。

Application

像往常一样,应用程序可以使用 ./mvnw cound package 进行打包并用 -runner.jar 文件执行。 您也可以使用 ./mvnw package -Pnative 来构建 native 可执行文件。

进一步

Hibernate Validator extension 和 CDI

Hibernate Validator extension 与 CDI 密切结合。

配置 ValidatorFactory

有时,您可能需要配置 ValidatorFactory 的行为,例如使用特定的 ParameterNameProvider

ValidatorFactory 是由 Quarkus 本身实例化的。 您可以容易地调整为用在配置中注入的 beans 来替代它。

如果您在应用程序中创建了以下类型的 bean,它将被自动注入 ValidatorFactory 配置:

  • javax.validation.ClockProvider

  • javax.validation.ConstraintValidator

  • javax.validation.ConstraintValidatorFactory

  • javax.validation.MessageInterpolator

  • javax.validation.ParameterNameProvider

  • javax.validation.TraversableResolver

  • org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy

  • org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory

You don’t have to wire anything.

显然,对于列出的每一种类型,你只能声明一个 bean。

应宣布这些 bean 为 @ApplicationScoped

作为 beans 的约束验证器

可以声明您的约束验证器为 CDI bean:

@ApplicationScoped
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, String> {

    @Inject
    MyService service;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }

        return service.validate(value);
    }
}

初始化给定类型的约束验证器时, Quarkus 将检查这种类型的 bean 是否可用,如果可用将使用它而不是实例化一个。

因此,正如我们的例子所显示的那样,您可以在约束验证器 bean 中充分使用注入。

除了非常特殊的情况外,建议将上述 bean 声明为 @ApplicationScoped

验证和本地化

默认情况下,将使用构建系统语言返回约束违背消息。

您可以通过 application.properties 中添加以下配置来调整:

# The default locale to use
quarkus.default-locale=fr-FR

如果您正在使用 RESTEasy, 在 JAX-RS 接口上, Hibernate Validator 将自动从 HTTP 头 Accept-Language 推断最合适的语言, 已支持的 locale 已经在 application.properties 指定。

# The list of all the supported locales
quarkus.locales=en-US,es-ES,fr-FR
quarkus.pro 是基于 quarkus.io 的非官方中文翻译站 ,最后更新 2020/04 。
沪ICP备19006215号-8
QQ交流群:1055930959
微信群: