JAVA裏自定義註解來進行數據驗證

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

相關文章
相關標籤/搜索