Spring Boot參數校驗以及分組校驗的使用

簡介:作web開發基本上每一個接口都要對參數進行校驗,若是參數比較少,還比較容易處理,一但參數比較多了的話代碼中就會出現大量的if-else語句。雖然這種方式簡單直接,但會大大下降開發效率和代碼可讀性。因此咱們可使用validator組件來代替咱們進行沒必要要的coding操做。本文將基於validator的介紹資料,同時結合做者本身在項目中的實際使用經驗進行了總結。

 title=

做者 | 江巖
來源 | 阿里技術公衆號前端

一 前言

作web開發有一點很煩人就是要對前端輸入參數進行校驗,基本上每一個接口都要對參數進行校驗,好比一些非空校驗、格式校驗等。若是參數比較少的話仍是容易處理的一但參數比較多了的話代碼中就會出現大量的if-else語句。java

使用這種方式雖然簡單直接,可是也有很差的地方,一是下降了開發效率,由於咱們須要校驗的參數會存在不少地方,而且不一樣地方會有重複校驗,其次下降了代碼可讀性,由於在業務代碼中摻雜了太多額外工做的代碼。web

因此咱們可使用validator組件來代替咱們進行沒必要要的coding操做。本文基於validator的介紹資料,也結合本身在項目中的實際使用經驗進行了總結,但願能幫到你們。spring

1 什麼是validator

Bean Validation是Java定義的一套基於註解的數據校驗規範,目前已經從JSR 303的1.0版本升級到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成於2017.08),已經經歷了三個版本 。須要注意的是,JSR只是一項標準,它規定了一些校驗註解的規範,但沒有實現,好比@Null、@NotNull、@Pattern等,它們位於 javax.validation.constraints這個包下。而hibernate validator是對這個規範的實現,並增長了一些其餘校驗註解,如 @NotBlank、@NotEmpty、@Length等,它們位於org.hibernate.validator.constraints這個包下。segmentfault

若是咱們的項目使用了Spring Boot,hibernate validator框架已經集成在 spring-boot-starter-web中,因此無需再添加其餘依賴。若是不是Spring Boot項目,須要添加以下依賴。app

 title=

二 註解介紹

1 validator內置註解

 title=

hibernate validator中擴展定義了以下註解:框架

 title=

三 使用

使用起來比較簡單,都是使用註解方式使用。具體來講分爲單參數校驗、對象參數校驗,單參數校驗就是controller接口按照單參數接收前端傳值,沒有封裝對象進行接收,若是有封裝對象那就是對象參數校驗。spring-boot

1 單參數校驗

單參數校驗只須要在參數前添加註解便可,以下所示:post

public Result deleteUser(@NotNull(message = "id不能爲空") Long id) {
  // do something
}

但有一點須要注意,若是使用單參數校驗,controller類上必須添加@Validated註解,以下所示:大數據

@RestController
@RequestMapping("/user")
@Validated // 單參數校驗須要加的註解
public class UserController {
  // do something
}

2 對象參數校驗

對象參數校驗使用時,須要先在對象的校驗屬性上添加註解,而後在Controller方法的對象參數前添加@Validated 註解,以下所示:

public Result addUser(@Validated UserAO userAo) {
    // do something
}

public class UserAO {
  @NotBlank
  private String name;

  @NotNull
  private Integer age;
  
  ……
}

註解分組

在對象參數校驗場景下,有一種特殊場景,同一個參數對象在不一樣的場景下有不一樣的校驗規則。好比,在建立對象時不須要傳入id字段(id字段是主鍵,由系統生成,不禁用戶指定),可是在修改對象時就必需要傳入id字段。在這樣的場景下就須要對註解進行分組。

1)組件有個默認分組Default.class, 因此咱們能夠再建立一個分組UpdateAction.class,以下所示:

public interface UpdateAction {
}

2)在參數類中須要校驗的屬性上,在註解中添加groups屬性:

public class UserAO {

    @NotNull(groups = UpdateAction.class, message = "id不能爲空")
    private Long id;
    
    @NotBlank
    private String name;

    @NotNull
    private Integer age;
    
    ……
}

如上所示,就表示只在UpdateAction分組下校驗id字段,在默認狀況下就會校驗name字段和age字段。

而後在controller的方法中,在@Validated註解裏指定哪一種場景便可,沒有指定就表明採用Default.class,採用其餘分組就須要顯示指定。以下代碼便表示在addUser()接口中按照默認狀況進行參數校驗,在updateUser()接口中按照默認狀況和UpdateAction分組對參數進行共同校驗。

public Result addUser(@Validated UserAO userAo) {
  // do something
}
public Result updateUser(@Validated({Default.class, UpdateAction.class}) UserAO userAo) {
  // do something
}

對象嵌套

若是須要校驗的參數對象中還嵌套有一個對象屬性,而該嵌套的對象屬性也須要校驗,那麼就須要在該對象屬性上增長@Valid註解。

public class UserAO {

    @NotNull(groups = UpdateAction.class, message = "id不能爲空")
    private Long id;
    
    @NotBlank
    private String name;

    @NotNull
    private Integer age;
    
    @Valid
    private Phone phone;
    
    ……
}

public class Phone {
    @NotBlank
    private String operatorType;
    
    @NotBlank
    private String phoneNum;
}

3 錯誤消息的捕獲

參數校驗失敗後會拋出異常,咱們只須要在全局異常處理類中捕獲參數校驗的失敗異常,而後將錯誤消息添加到返回值中便可。捕獲異常的方法以下所示,返回值Result是咱們系統自定義的返回值類。

@RestControllerAdvice(basePackages= {"com.alibaba.dc.controller","com.alibaba.dc.service"})
public class GlobalExceptionHandler {

  @ExceptionHandler(value = {Throwable.class})
  Result handleException(Throwable e, HttpServletRequest request){
    // 異常處理
        }
}

須要注意的是,若是缺乏參數拋出的異常是MissingServletRequestParameterException,單參數校驗失敗後拋出的異常是ConstraintViolationException,get請求的對象參數校驗失敗後拋出的異常是BindException,post請求的對象參數校驗失敗後拋出的異常是MethodArgumentNotValidException,不一樣異常對象的結構不一樣,對異常消息的提取方式也就不一樣。以下圖所示:

1)MissingServletRequestParameterException

if(e instanceof MissingServletRequestParameterException){
    Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
    String msg = MessageFormat.format("缺乏參數{0}", ((MissingServletRequestParameterException) e).getParameterName());
    result.setMessage(msg);
    return result;
}

2)ConstraintViolationException異常

if(e instanceof ConstraintViolationException){
  // 單個參數校驗異常
  Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
  Set<ConstraintViolation<?>> sets = ((ConstraintViolationException) e).getConstraintViolations();
  if(CollectionUtils.isNotEmpty(sets)){
    StringBuilder sb = new StringBuilder();
    sets.forEach(error -> {
                    if (error instanceof FieldError) {
                        sb.append(((FieldError)error).getField()).append(":");
                    }
                    sb.append(error.getMessage()).append(";");
                });
    String msg = sb.toString();
    msg = StringUtils.substring(msg, 0, msg.length() -1);
    result.setMessage(msg);
  }
  return result;
}

3)BindException異常

if (e instanceof BindException){
      // get請求的對象參數校驗異常
      Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
      List<ObjectError> errors = ((BindException) e).getBindingResult().getAllErrors();
      String msg = getValidExceptionMsg(errors);
      if (StringUtils.isNotBlank(msg)){
        result.setMessage(msg);
      }
      return result;
}
private String getValidExceptionMsg(List<ObjectError> errors) {
  if(CollectionUtils.isNotEmpty(errors)){
    StringBuilder sb = new StringBuilder();
    errors.forEach(error -> {
                    if (error instanceof FieldError) {
                       sb.append(((FieldError)error).getField()).append(":");
                    }
                    sb.append(error.getDefaultMessage()).append(";");
                });
    String msg = sb.toString();
    msg = StringUtils.substring(msg, 0, msg.length() -1);
    return msg;
  }
  return null;
}

4)MethodArgumentNotValidException異常

if (e instanceof MethodArgumentNotValidException){
      // post請求的對象參數校驗異常
      Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
      List<ObjectError> errors = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors();
      String msg = getValidExceptionMsg(errors);
      if (StringUtils.isNotBlank(msg)){
        result.setMessage(msg);
      }
      return result;
}

《一站式大數據開發治理DataWorks使用寶典》官方電子書開放下載

DataWorks官方入門電子書出版啦,零基礎入門大數據開發治理,全面瞭解DataWorks十大功能模塊,快速上手DataWorks核心功能。

點擊這裏,便可下載~

本文內容由阿里雲實名註冊用戶自發貢獻,版權歸原做者全部,阿里雲開發者社區不擁有其著做權,亦不承擔相應法律責任。具體規則請查看《阿里雲開發者社區用戶服務協議》和《阿里雲開發者社區知識產權保護指引》。若是您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將馬上刪除涉嫌侵權內容。
相關文章
相關標籤/搜索