使用validator-api來驗證spring-boot的參數

做爲服務端開發,驗證前端傳入的參數的合法性是一個必不可少的步驟,可是驗證參數是一個基本上是一個體力活,並且冗餘代碼繁多,也影響代碼的可閱讀性,因此有沒有一個比較優雅的方式來解決這個問題?html

這麼簡單的問題固然早就有大神遇到而且解決了,這一篇文章主要講一下解決基於spring-boot的驗證參數的比較好的方法:利用validator-api來進行驗證參數。前端

spring-boot-starter-web包裏面有hibernate-validator包,它提供了一系列驗證各類參數的方法,因此說spring-boot已經幫咱們想好要怎麼解決這個問題了。web

這篇文章針對spring-boot裏面的spring-mvc介紹三種方式來驗證參數。spring

(一):這個方法在網上大部分均可以查到,先假設咱們的restful的接口接受一個GradeAndClassroomModel類型的對象,而且這個類被定義成api

@Data
public class GradeAndClassroomModel {  
@Range(min = 1, max = 9, message = "年級只能從1-9")  
private int grade;  
@Range(min = 1, max = 99, message = "班級只能從1-99")  
private int classroomNumber;
}

利用validator提供的一系列註解,好比本例中的@Range,就能夠表示參數的範圍和出錯時候的提示信息。還有不少其餘註解,這裏就不一一列出spring-mvc

而後咱們的Controller層的代碼爲restful

@RequestMapping(value = "/paramErrorTest", method = RequestMethod.GET)
public String paramErrorTest(    
  @Valid    
  @ModelAttribute    
  GradeAndClassroomModel gradeAndClassroomModel, 
  BindingResult result) {  
  return classroomService.getTeacherName(gradeAndClassroomModel.getGrade(), gradeAndClassroomModel.getClassroomNumber());
}

其中若是驗證出錯,result對象裏面就會有錯誤信息,而後能夠本身進行處理。mvc

(二): 針對上面的例子,會有人說,就兩個參數,爲何要做爲對象呢?會不會太麻煩?確實,若是隻有少數對象,直接把參數寫到Controller層,而後在Controller層進行驗證就能夠了。app

@RequestMapping(value = "/teacherName", method = RequestMethod.GET)
public String teacherName(
  @Range(min = 1, max = 9, message = "年級只能從1-9")        
  @RequestParam(name = "grade", required = true) 
  int grade,  
  @Min(value = 1, message = "班級最小隻能1")    
  @Max(value = 99, message = "班級最大隻能99")      
  @RequestParam(name = "classroom", required = true)    
  int classroom) {  
return classroomService.getTeacherName(grade, classroom);
}

若是直接把validator提供的註解移除來寫到請求參數上面的話是否是就能夠了呢?答案是錯,爲何這樣不能成功的驗證參數呢?具體緣由你們能夠參考官方文檔:http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/htmlsingle/#validation-beanvalidation-spring-method框架

上面的文檔已經說的很清楚了,因此咱們須要建立一個Bean

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {  
  return new MethodValidationPostProcessor();
}

而後在類方法上面加上註解@Validated

@RestController
@RequestMapping("/spring-boot/classroom")
@Validated
public class ClassroomController {
 ...
}

而後以前沒有生效的註解@Range@Min@Maxvalidator包裏面提供的註解就能夠生效了。

(三)估計到了這裏又會有人問,若是validator包裏面註解不能知足咱們的需求,咱們是否能夠本身定義參數驗證的邏輯。答案是確定的,咱們能夠利用

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Constraint(validatedBy = {Validator.class})
public @interface ParamValidator {

  String message() default "Parameter error!";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};

}

public class Validator implements ConstraintValidator<ParamValidator, Object> {
  ...
}

組合進行自定義,具體的例子網上其餘文章就不少了,這裏就不進行詳細的例子了,可是最終使用的時候就是

@RequestMapping(value = "/paramValidator", method = RequestMethod.GET)
  public String paramValidator(
      @ParamValidator(isRequired = true, desc = "年級", range = "int:1~9", message = "年級只能從1-9")
      @RequestParam(name = "grade", required = true)
      int grade,
      @ParamValidator(isRequired = true, desc = "班級", range = "int:1~99", message = "班級只能從1-99")
      @RequestParam(name = "classroom", required = true)
      int classroom) {
    return classroomService.getTeacherName(grade, classroom);
  }

另外不要忘記方法二裏面裏面提到的MethodValidationPostProcessor這個bean,若是沒有初始化這個bean,自定義的驗證方法也不會執行。驗證邏輯會失效。

是否是經過這樣寫註解的方式來驗證進行請求的參數,代碼邏輯更佳清晰和優雅?表達的含義也會更佳清楚?而且沒有了大量重複的相似的驗證代碼。

Ps:這裏的代碼都是基於spring-mvc框架來試驗的,若是有人並無使用spring-mvc做爲rest框架,而是使用jersey來做爲rest框架的話,可能一些細節方面須要調整, 可是這三種方案應該都是能夠兼容的。

最後若是以爲所講的東西可以幫助到你,而且但願瞭解更多的知識,進行更詳細的深刻的學習,歡迎加羣632109190進行討論和學習。

相關文章
相關標籤/搜索