1.5.孤城暗雪:验证

青海长云暗雪山,孤城遥望玉门关。——王昌龄《从军行七首·其四》

「壹」JSR303

    本章我们进入Zero中的另外一大亮点——对JSR303的支持;JSR303是Java EE 6开始出现的一项子规范,又称为Bean Validation,它提供了后端执行请求校验的基本规范,而常用的一个该规范的实现就是Hibernate Validator,Zero中则使用了它来实现该规范,并且对之进行了深度扩展。

    接下来的讲解中,我们将忽略控制台中大部分输出,而关注异常响应部分来看Zero中对JSR部分的支持。Zero对JSR303支持的使用场景如下:

  1. JSR303可以在Zero中的Agent组件中使用。

  2. 扩展了JSR303过后,Zero可针对Json结构的数据进行验证(配合yaml配置文件)。

在Zero中一个标准的Verticle组件分两种:Agent组件和Worker组件,后续章节会逐步讲解异步模式下的开发,让读者逐渐了解这两种组件。

    关于Zero中对JSR303的部分有两个限制:

  1. 在对Json结构的数据执行验证时,验证的数据规范和zero-ui前端一致,一方面可以和它无缝集成,另外一方面可以独立使用。

  2. JSR303在执行Json结构数据验证时,需配合yml格式的接口描述文件执行验证定义描述。

1.1. JSR303注解

    JSR303中的注解如下:

注解位于包javax.validation.constraints中。

注解名
说明

@Null

注释的元素必须为null。

@NotNull

注释的元素必须不为null。

@AssertTrue

注释的元素必须是true。

@AssertFalse

注释的元素必须是false。

@Min(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值。

@Max(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值。

@DecimalMin(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值。

@DecimalMax(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值。

@Size(max,min)

被注释的元素大小必须在指定的范围内。

@Digits(integer,fraction)

被注释的元素必须是数值,且精度是可接受的范围。

@Past

被注释的元素必须是一个过去的时间。

@Future

被注释的元素必须是一个将来的时间。

@Pattern(value)

被注释的元素必须符合指定的正则表达式。

1.2. 环境准备

1.2.1. Hibernate Validator基础配置

    Hibernate Validator的默认配置文件位于资源文件ValidationMessages.properties ,在Zero中,默认的文件名修改成vertx-validation.properties,同样支持国际化。在Maven结构项目中,仅需在src/main/resources 目录中引入vertx-validation.properties配置文件配置验证过程中的文字说明。

    如果要设置验证信息,则可在资源目录中添加属性文件vertx-validation.properties,而该文件在处理中文编码时需注意:

  • 如果编辑器使用的编码是UTF-8(推荐),则可直接在.properties中输入中文。

  • 如果编辑器使用的编码是ISO-8859-1(系统默认),则使用native2ascii工具直接将中文字符串转换成Unicode格式。

1.2.2. 响应规范

    Zero中严格遵循HTTP应用层协议,当验证失败时,会生成HTTP状态代码为400(Bad Request)的错误响应,该响应的基础格式如:

    三节点的含义如下:

属性名
类型
含义

code

负整数

系统内部错误码,通常是小于-10000的负数。

message

字符串

「Debug」系统错误描述。

info

字符串

客户端专用异常信息,如验证异常会显示验证信息在前端。

1.3. JSR303示例

1.3.1. @Null/@NotNull

    必填和可选专用注解,参考下边示例:

    您可以在up-apollo项目中找到该源代码,上述示例中对应的配置文件内容如下:

    发送下边请求:

    您就可以得到一个HTTP状态代码为400(Bad Request)的响应:

有关JSR303和Hibernate-Validator的信息读者可以参考相关教程,这里就不重述了,但后续示例会对JSR303的基础注解提供部分参考代码。

1.3.2. @AssertTrue/@AssertFalse

    布尔类型专用注解,参考下边示例:

    发送下边请求:

    于是您将得到如下响应:

    也许读者会困惑,为什么看起来验证并没有生效呢?主要是该参数并没有配合@NotNull 来验证必填性质。Zero中默认是所有参数都可选的,即Optional,当您要求单个参数必填时,则需进一步执行@NotNull限定,不仅仅是@AssertTrue,@AssertFalse的注解,其他所有JSR303的基础注解都依赖@NotNull限定来区分参数的** 必填可选**。

    加入了@NotNull过后,发送请求:

    您就得到了400 Bad Request的响应信息:

1.3.3. @Min/@Max

    数值类型专用注解,参考下边示例:

    发送下边请求:

    您就得到了400 Bad Request的响应信息:

1.3.4. @DecimalMin/@DecimalMax

    浮点类型专用注解,参考下边示例:

    发送下边请求:

    您就得到了400 Bad Request的响应信息:

注:这两个注解的字面量使用的是java.lang.String类型,而并不是java.lang.Double类型,简言之,只要字面量可转换成合法浮点数,那么就可以设置到注解的value中。

1.3.5. @Size

    字符串专用注解,参考下边示例:

    发送下边请求:

    您就得到了400 Bad Request的响应信息:

1.3.6. @Digits

    浮点数精度专用注解,参考下边示例:

    发送下边请求:

    您就得到了400 Bad Request的响应信息:

1.3.7. @Future/@Post

    时间专用注解,参考下边示例:

    发送下边请求:

    您就得到了400 Bad Request的响应信息:

1.3.8. @Pattern

    正则表达式专用注解,参考下边示例:

    发送下边请求:

    您就得到了400 Bad Request的响应信息:

    该示例中不仅使用了JSR303原生的@Pattern注解,还使用了Hibernate Validator中扩展的JSR303的相关注解,它扩展的常用注解如下:

注解名
说明

@Email

被注解的元素必须是电子邮件地址。

@Length(min,max)

被注解的字符串大小必须在指定的范围内。

@NotEmpty

被注解的字符串必须非空。

@Range(min,max)

被注解的元素必须在合适的范围内。

@NotBlank

被注解的字符串必须非空。

@URL(protocol,host,port,regexp,flags)

被注释的字符串必须是一个有效的URL。

@CreditCardNumber

被注释的字符串必须通过Luhn校验算法(银行卡、信用卡)。

@ScriptAssert(lang,script,alias)

必须有JSR 223规范支持。

@SafeHtml(whitelistType,additionalTags)

Class类类路径中有jsonup包。

详细用法参考Hibernate Validator官方文档。

1.3.9. Pojo模式

    除了上边的单独参数的校验以外,Zero中同样借着Hibernate Validator支持Pojo类型的Java类的校验,参考下边示例代码:

JavaJson类

PojoAgent主类

    发送下边请求:

    请求内容如:

    您就得到了400 Bad Request的响应信息:

    倘若您直接改成下边请求就可以发送成功了:

该示例中使用了lombok库操作Pojo,并且使用了Zero对JSR303的扩展注解@BodyParam。

「贰」JSR303扩展

2.1. 现存问题

    在开发企业级项目过程中,JSR303的使用也许远远不够,它主要可以解决下边两个场景的使用:

  • 普通零散参数的验证。

  • 参数直接可转换成Pojo的参数的验证。

    但是还有很多场景可能无法满足某些基本需求,例如:

  • 无固定Pojo结构的复杂参数验证,如JsonObject/JsonArray

  • 集合结构的验证。

  • 上传文件的验证。

  • 自定义验证。

2.2. Zero扩展

    Zero框架对JSR303进行了实现层的扩展,这些扩展让Zero在处理前文提到的问题时羽翼更加丰富了,关于入参的注解此处就不详细描述了,这部分在前边一个章节已经提及。

2.2.1. 启用配置

    本章节会使用一个特殊的Zero注解:io.vertx.up.annotations.Codex,该注解表示当前结构会启用Zero中的扩展验证功能。启用了@Codex 注解过后,需要在项目的资源目录中提供验证配置文件:

  • 配置文件目录:src/main/resources/codex/

  • 文件名基础规则:

    • 基本文件名 = <api>.<method>.yml

    • api中的/直接被.符号替换。

    • api中如果出现了:name路径参数,则:被替换成$符号而生效。

    在对应目录中创建示例中所需的验证规则文件hi.jsr303.advanced.post.yml

    该文件的内容如下:

2.2.2. 示例代码

    参考下边的示例代码:

    发送下边请求:

    请求内容如:

    您就得到了400 Bad Request的响应信息:

2.3. Zero功能支持

    上述小节提供了基本的示例让读者对@Codex有了一定的了解,那么Zero对这种验证究竟扩展到何等程度呢——我相信这是读者最关心的。

2.3.1. 参数类型

    细心的读者会发现,@Codex只会作用于部分复杂结构,这种复杂结构主要包括:

类名
含义

io.vertx.core.json

JsonObject

Json对象类型。

io.vertx.core.json

JsonArray

Json数组类型。

io.vertx.ext.web

FileUpload

Vertx中的文件上传类型。

java.io

File

Java中的文件对象(Zero直接支持该参数)。

简单说上述类型作为参数时就可以直接使用@Codex注解。

2.3.2. 规则分类

    上述文件hi.jsr303.advanced.post.yml中为不同的字段定义了验证规则,字段的验证规则可支持多个,验证时按照定义顺序依次验证。

    每种类型的公共配置如:

配置名
含义

type

规则类型,不同类型决定了配置参数以及验证执行器。

message

验证失败响应消息,最终呈现在界面中的文字信息。

Zero中支持的类型表如下:

类型(type)
配置表
含义

required

验证某个属性是否必须。

length

min, max

验证某个字符串长度是否在某个范围。

minlength

min

验证某个字符串最小长度(length的快速版)。

maxlength

max

验证某个字符串最大长度(length的快速版)。

empty

「集合专用」验证某个集合是否为空。

singlefile

「上传专用」验证上传文件是否为单个文件,多个文件非法。

「叄」总结

    本章主要介绍了Zero对JSR303规范的支持、扩展和相关示例:

  • Zero支持JSR303规范中的大部分验证注解(Hibernate-Validator实现)。

  • Zero对JSR303规范进行了扩展,支持复杂数据结构的验证。

Last updated