API 开发中经常会遇到一些对请求数据进行验证的情况, 这时候如果使用注解就有两个好处, 一是验证逻辑和业务逻辑分离, 代码清晰, 二是验证逻辑可以轻松复用, 只需要在要验证的地方加上注解就可以.
Java 提供了一些基本的验证注解, 比如 @NotNull,@Size, 但是更多情况下需要自定义验证逻辑, 这时候就可以自己实现一个验证注解, 方法很简单, 仅需要两个东西:
一个自定义的注解, 并且指定验证器
一个验证器的实现
自定义验证注解
考虑有一个 API, 接收一个 Student 对象, 并希望对象里的 age 域的值是奇数, 这时候就可以创建以下注解:
- @Target(ElementType.FIELD)
- @Retention(RetentionPolicy.RUNTIME)
- @Constraint(validatedBy = AgeValidator.class)
- public @interface Odd {
- String message() default "Age Must Be Odd";
- Class<?>[] groups() default {};
- Class<? extends Payload>[] payload() default {};
- }
其中:
@Target 指明这个注解要作用在什么地方, 可以是对象, 域, 构造器等, 因为要作用在 age 域上, 因此这里选择 FIELD
@Retention 指明了注解的生命周期, 可以有 SOURCE(仅保存在源码中, 会被编译器丢弃),CLASS(在 class 文件中可用, 会被 VM 丢弃) 以及 RUNTIME(在运行期也被保留), 这里选择了生命周期最长的 RUNTIME
@Constraint 是最关键的, 它表示这个注解是一个验证注解, 并且指定了一个实现验证逻辑的验证器
message() 指明了验证失败后返回的消息, 此方法为 @Constraint 要求
groups() 和 payload() 也为 @Constraint 要求, 可默认为空, 详细用途可以查看 @Constraint 文档
创建验证器
有了注解之后, 就需要一个验证器来实现验证逻辑:
- public class AgeValidator implements ConstraintValidator<Odd,Integer> {
- @Override
- public void initialize(Odd constraintAnnotation) {
- }
- @Override
- public boolean isValid(Integer age, ConstraintValidatorContext constraintValidatorContext) {
- return age % 2 != 0;
- }
- }
其中:
验证器有两个类型参数, 第一个是所属的注解, 第二个是注解作用地方的类型, 这里因为作用在 age 上, 因此这里用了 Integer
initialize() 可以在验证开始前调用注解里的方法, 从而获取到一些注解里的参数, 这里用不到
isValid() 就是判断是否合法的地方
应用注解
注解和验证器创建好之后, 就可以使用注解了:
- public class Student {
- @Odd
- private int age;
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
- @RestController
- public class StudentResource {
- @PostMapping("/student")
- public String addStudent(@Valid @RequestBody Student student) {
- return "Student Created";
- }
- }
在需要启用验证的地方加上 @Valid 注解, 这时候如果请求里的 Student 年龄不是奇数, 就会得到一个
400
响应:
- {
- "timestamp": "2018-08-15T17:01:44.598+0000",
- "status": 400,
- "error": "Bad Request",
- "errors": [
- {
- "codes": [
- "Odd.student.age",
- "Odd.age",
- "Odd.int",
- "Odd"
- ],
- "arguments": [
- {
- "codes": [
- "student.age",
- "age"
- ],
- "arguments": null,
- "defaultMessage": "age",
- "code": "age"
- }
- ],
- "defaultMessage": "Age Must Be Odd",
- "objectName": "student",
- "field": "age",
- "rejectedValue": 12,
- "bindingFailure": false,
- "code": "Odd"
- }
- ],
- "message": "Validation failed for object='student'. Error count: 1",
- "path": "/student"
- }
也可以手动来处理错误, 加上一个 BindingResult 来接收验证结果即可:
- @RestController
- public class StudentResource {
- @PostMapping("/student")
- public String addStudent(@Valid @RequestBody Student student, BindingResult validateResult) {
- if (validateResult.hasErrors()) {
- return validateResult.getAllErrors().get(0).getDefaultMessage();
- }
- return "Student Created";
- }
- }
这时候如果验证出错, 便只会返回一个状态为
200
, 内容为 Age Must Be Odd 的响应.
来源: https://www.cnblogs.com/xz816111/p/9484902.html