API开发中常常会遇到一些对请求数据进行验证的状况,这时候若是使用注解就有两个好处,一是验证逻辑和业务逻辑分离,代码清晰,二是验证逻辑能够轻松复用,只须要在要验证的地方加上注解就能够。app
Java提供了一些基本的验证注解,好比@NotNull
、@Size
,可是更多状况下须要自定义验证逻辑,这时候就能够本身实现一个验证注解,方法很简单,仅须要两个东西:ide
考虑有一个API,接收一个Student
对象,并但愿对象里的age
域的值是奇数,这时候就能够建立如下注解:this
@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 {}; }
其中:code
@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
来接收验证结果便可:get
@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的响应。编译器