内容

  • Bean Validation(JSR-303):介绍 Java Bean 验证、核心 API、实现框架 Hibernate Validator
  • Apache commons-validator :介绍最传统 Apache 通用验证器框架,如:长度、邮件等方式
  • Spring Validator:介绍 Spring 内置验证器 API、以及自定义实现

Bean Validation 1.1 JSR-303

Maven依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

命名规则(Since Spring Boot 1.4):Spring Boot大多数情况采用starter(启动器,包含一些自动装配的Spring组建),官方的命名规则:spring-boot-starter-{name}。非官方采用{name}-spring-boot-starter

代码示例

1
2
3
4
5
6
7
8
9
10
11
import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;
public class User {
@Max(value=10000)
private int id;
@NotNull
private String name;
private String cardNumber;

//ignore getter and setter
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.bai.springbootvalidation.domain.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;

@RestController
public class UserController {

@PostMapping("/user/save")
public User save(@Valid @RequestBody User user){
return user;
}
}

请求时,当id>10000或name为null时,会报错。

  • 优点
    • 节省代码。
  • 其它方式
    • API方式
      • Assert.hasText(user.getName(),"名称不能为空");
      • 不能为空,必须有文字
      • 返回500
    • JVM断言
      • assert user.getId() <= 10000;
      • 返回400
    • 缺点
      • 耦合了业务逻辑,虽然可以通过HandlerInterceptorFilter做拦截,但是也是非常不优雅的。
      • 还可以通过AOP方式,也可以提升代码的可读性。
      • 以上方式都不是统一的标准

自定义Bean Validation

通过员工的卡号来校验,需要通过工号的前缀和后缀来判断。前缀必须以2BAI-开头。后缀必须以数字结尾。需要通过Bean Validation校验。

实现步骤

  • 复制成熟 Bean Validation Annotation的模式
1
2
3
4
5
6
@Target(FIELD)
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {})
public @interface ValidCardNumber {
}
  • 参考和理解@Constraint
  • 实现ConstraintValidator 接口
  • 将实现ConstraintValidator 接口 定义到@Constraint#validatedBy
  • 给@ValidCardNumber 添加 message 参数

补充内容

  • org.springframework.util.StringUtils.delimitedListToStringArray来分割字符串取代Split
    • 该方法使用了正则表达式
    • 其次是空指针NPE保护不够
    • 如果在依赖中没有StringUtils.delimitedListToStringArray,可以使用JDK里的StringTokenizer(不足:类似于枚举Enumeration)或org.apache.commons.lang3.StringUtils
  • 采用三方类库时,尽量用一套类库,不要A库用一点,B库用一点。

Q&A

Q:JSON校验如何办?

A:尝试变成 Bean 的方式。

Q:实际中很多参数都要校验,这样写会增加很多类

A:确实会增加部分工作量,大多数场景,不需要自定义,除非很特殊情况。Bean Validation 的主要缺点,单元测试不方便。需要拿到Annotation,然后构造参数去测试。

Q:如果前端固定表单的话,这种校验方式很好。但是灵活性不够,如果表单是动态的话,如何校验呢?

A:表单字段与 Form 对象绑定即可,再走 Bean Validation 逻辑。

1
2
3
4
5
<form action="" method="POST" command="form">
<input value="${form.name}" />
...
<input value="${form.age}" />
</form>

也可以通过责任链模式(Pipeline),一个接一个验证:

field 1-> field 2 -> field 3 -> compute -> result

Q:如何自定义,返回格式?如何最佳实现?

A:可以通过REST来实现,比如 XML 或者 JSON 的格式(视图)。