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的響應。編譯器