Web 与 HTTP 服务
你当前正在浏览历史版本 Vert.x 5.1.10 的文档。 访问本页面的 最新版本。

Vert.x Web Validation 参数与请求体验证

Vert.x Web Validation 帮助你高效解析并校验传入 HTTP 请求的各项入参(Parameters)与请求体(Bodies)。

核心特性支持:

  • 深度解析并校验各类请求参数(支持序列化展开 exploded 与深层嵌套对象)

  • 解析并校验请求体,包括 JSON 报文与各种 Form 表单

  • 支持配置请求前置断言谓词(Request Predicates)

  • 允许在同一路由下支持多种不同格式的请求体,并进行统一一致的解析与校验

  • 支持自定义扩展解析与校验规则

  • 统一拦截并结构化管理解析与校验失败异常

底层基于 Vert.x Json Schema 定义请求参数与请求体的模式规范。

使用 Vert.x Web Validation

在项目构建配置中添加以下依赖:

  • Maven(在 pom.xml 中):

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-web-validation</artifactId>
  <version>5.1.10</version>
</dependency>
  • Gradle(在 build.gradle 文件中):

dependencies {
  compile 'io.vertx:vertx-web-validation:5.1.10'
}

对比:未使用 Web Validation 时的样板代码

当接收到一个 HTTP 请求时,通常需要编写大量繁琐的手动类型转换与边界校验:

router
  .get("/user")
  .handler(routingContext -> {
    // 获取原始字符串参数
    String aParamUnparsed = routingContext.queryParam("aParam").get(0);
    if (aParamUnparsed == null) {
      routingContext.fail(400);
      return;
    }
    // 手动解析整型
    int aParam;
    try {
      aParam = Integer.parseInt(aParamUnparsed);
    } catch (NumberFormatException e) {
      routingContext.fail(400, e);
      return;
    }
    // 业务数值校验:例如不能大于 100
    if (aParam > 100) {
      routingContext.fail(400);
      return;
    }

    // 参数校验准备完毕后,才能开始聚焦核心业务逻辑
  });

而 Vert.x Web Validation 提供了极具表达力的流式 API,让你声明式地完成校验:

router
  .get("/user")
  .handler(
    ValidationHandlerBuilder
      .create(schemaRepository)
      .queryParameter(param(
        "aParam",
        intSchema().with(maximum(100))
      ))
      .build()
  )
  .handler(routingContext -> {
    RequestParameters parameters =
      routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY);
    int aParam = parameters.queryParameter("aParam").getInteger();
    // 直接使用强类型合法参数,聚焦核心业务逻辑
  });

创建 ValidationHandler

该模块提供了简洁明了的 Builder API 用于构建 ValidationHandler。该 Handler 负责自动拦截请求并执行解析与校验。 调用 ValidationHandlerBuilder.create 即可初始化构建器。

定义参数规则 (Defining parameters)

支持针对 HTTP 请求的四个不同位置定义参数:Query 查询串、Cookie、Header 请求头以及 URL Path 路径。

每个参数由 ParameterProcessor 承载,可通过 Parameters 工具类快速声明:

ValidationHandlerBuilder
  .create(schemaRepository)
  .pathParameter(Parameters.param("myPathParam", stringSchema()))
  .queryParameter(Parameters.optionalParam("myQueryParam", intSchema()));

自定义错误提示信息 (Defining custom error messages)

校验失败提示信息支持自由定制:

Header 和 Path 通常只支持简单单值参数,而 Query 与 Cookie 则原生支持诸如数组展开(exploded)以及深度嵌套对象(deepObject):

ValidationHandlerBuilder
  .create(schemaRepository)
  .queryParameter(Parameters.explodedParam(
    "myArray",
    arraySchema().items(stringSchema())
  ))  // 接收 myArray=item1&myArray=item2 数组展开格式
  .queryParameter(Parameters.deepObjectParam(
    "myDeepObject",
    objectSchema()
      .property("name", stringSchema())
  )); // 接收 myDeepObject[name]=francesco 对象嵌套格式

定义请求体验证 (Defining request bodies)

每种请求体类型根据 Content-Type 请求头进行分流匹配。 你可以为同一个 ValidationHandler 同时挂载多种受支持的请求体处理器。 若未匹配到已配置的 Body 处理器,默认不会直接报错,除非配置了下文提到的“必须包含请求体”断言。

通过 Bodies 便捷声明:

ObjectSchemaBuilder bodySchemaBuilder = objectSchema()
  .property("username", stringSchema())
  .property("password", stringSchema());
ValidationHandlerBuilder
  .create(schemaRepository)
  .body(Bodies.json(bodySchemaBuilder))
  .body(Bodies.formUrlEncoded(bodySchemaBuilder));

在上述配置中,ValidationHandler 能同时无缝兼容 JSON 报文与传统 URL 编码表单。 特别是 Form 表单会被自动转换为 JSON 统一结构,业务代码无需关心底层客户端究竟是以 Form 还是以 JSON 形式提交。

定义请求前置断言 (Defining request predicates)

通过 RequestPredicate 可以对请求设置强制前置条件。 例如要求该路由请求体必填:

ValidationHandlerBuilder
  .create(schemaRepository)
  .predicate(RequestPredicate.BODY_REQUIRED);

构建与挂载 ValidationHandler

配置好所有参数、请求体与前置断言后,调用 build() 生成 Handler 并挂载到路由上:

router
  .get("/user")
  .handler(
    ValidationHandlerBuilder
      .create(schemaRepository)
      .build()
  );

在业务中读取解析后的强类型数据

ValidationHandler 会将验证合规的参数统一写入 RoutingContext:

router
  .get("/user")
  .handler(
    ValidationHandlerBuilder
      .create(schemaRepository)
      .queryParameter(Parameters.explodedParam(
        "myArray",
        arraySchema().items(stringSchema())
      ))
      .body(Bodies.json(objectBodySchemaBuilder))
      .body(Bodies.formUrlEncoded(objectBodySchemaBuilder))
      .build()
  ).handler(routingContext -> {
    RequestParameters parameters =
      routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY);
    JsonArray myArray = parameters.queryParameter("myArray").getJsonArray();
    JsonObject body = parameters.body().getJsonObject();
  });

统一异常与失败管理 (Manage the failures)

每当 ValidationHandler 检测到反序列化异常或数据校验不通过时,会自动触发 RoutingContext.fail(400),并将 BadRequestException 的具体子类实例作为 cause 挂载在 context 异常中。 你可以通过 Vert.x Web 的 errorHandler 统一拦截定制 400 响应。

常见的异常细分子类包括:

拦截异常与响应示例:

router.errorHandler(400, routingContext -> {
  if (routingContext.failure() instanceof BadRequestException) {
    if (routingContext.failure() instanceof ParameterProcessorException) {
      // 处理参数校验失败异常
    } else if (routingContext.failure() instanceof BodyProcessorException) {
      // 处理请求体校验失败异常
    } else if (routingContext.failure() instanceof RequestPredicateException) {
      // 处理前置条件不满足异常
    }
  }
});

BadRequestException 还贴心地提供了 toJson 方法,可直接一键将校验失败明细转为 JSON 格式回写给调用方。

请注意:ValidationHandler 采用 快速失败(Fail-Fast) 设计原则,一旦检测到首个校验违背项,会立刻中断流程并通知 RoutingContext 抛出异常。