Spring-Validation(後端資料校驗) 你值得擁有

我恰芙蓉王發表於2020-07-08

前言

最近看到很多童鞋在專案中的對請求引數的校驗都用的if來判斷各引數的屬性,如:

if(StringUtils.isBlank(username)){
    return RR.exception("賬號不能為空");
}

if(StringUtils.isBlank(password)){
    return RR.exception("密碼不能為空");
}

if(StringUtils.isBlank(realName)){
    return RR.exception("姓名不能為空");
}
......

每個引數都需要這樣一個個去校驗null,返回對應資訊,程式碼就像疊羅漢一樣~~,在此,樓主強烈推薦一個神器:Validation,有了它,再也不用這樣去校驗引數啦,可以讓我們在專案中不用太關注其他東西,專注於業務邏輯的編寫。

 

引入核心依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

 

使用示例-新增使用者

需要校驗的實體類,在此使用了一些常用的校驗註解,基本上能夠見名知意,每個註解中都有message屬性,就是校驗不通過後的提示資訊

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "Account", description = "使用者實體類")
public class Account implements Serializable {

    private static final long serialVersionUID = -6310331730721778951L;
    
    private Long id;

    @NotBlank(message = "使用者名稱不能為空")
    @Length(max = 10, message = "使用者名稱最長為10位")
    private String name;

    @Pattern(regexp = PatternUtil.PASSWORDREGEX, message = "密碼為6-20位數字加字母組合")
    private String pwd;

    @NotBlank(message = "姓名不能為空")
    @Length(max = 8, message = "姓名最長為8位")
    private String realName;

    @Pattern(regexp = PatternUtil.PHONENOREGEX, message = "手機號碼格式不正確")
    private String phone;

    @NotNull(message = "使用者性別不能為空")
    @Max(value = 1,message = "性別 0:男 1:女")
    @Min(value = 0,message = "性別 0:男 1:女")
    private Integer sex;
    
    @NotNull(message = "使用者狀態不能為空")
    @Max(value = 1,message = "使用者狀態 0:啟用 1:禁用")
    @Min(value = 0,message = "使用者狀態 0:啟用 1:禁用")
    private Integer status;

    @Length(max = 200, message = "備註最長為200個字元")
    private String rmk;
}

 

controller介面,注意在接收引數前需加上@Validated註解,這樣就會逐個去校驗實體類中需要加了校驗註解的的屬性,完全通過才進入下一步業務處理,否則丟擲MethodArgumentNotValidException異常,在這裡我們直接將異常丟擲,交給全域性異常處理類來處理。

@SysLog(menu = "使用者管理", function = "新增",filterParams = {"pwd","password","salt"})
@PermissionCheck("account:manager:add")
@PostMapping(value = "/add", produces = BaseConsts.REQUEST_HEADERS_CONTENT_TYPE)
@ApiOperation(value = "使用者管理-新增使用者介面", notes = "使用者管理-新增使用者介面", httpMethod = BaseConsts.REQUEST_METHOD, response = RR.class)
public RR add(@Validated @RequestBody Account account) throws Exception {
    return RR.success("新增使用者成功");
}

 

宣告全域性異常處理類,處理所有異常,可以隨業務需要將異常種類細分,返回錯誤碼,返回提示資訊可自由定義...   這裡只需關注MethodArgumentNotValidException異常。

@RestControllerAdvice
public class GlobalExceptionHandle {

    /**
     * @param e
     * @return
     * @Description 未知異常處理
     */
    @ExceptionHandler(Exception.class)
    public RR handleException(Exception e) {
        e.printStackTrace();
        return RR.exception("系統異常,請聯絡管理員");
    }
    

    /**
     * @param e
     * @return
     * @Description 請求引數異常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public RR handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return RR.exception("請求引數錯誤:[" + e.getBindingResult().getFieldError().getDefaultMessage() + "]");
    }
    

    /**
     * @param e
     * @return
     * @Description 系統業務服務異常
     */
    @ExceptionHandler(ServerException.class)
    public RR handleServerException(ServerException e) {
        return RR.exception(e.getMessage());
    }
    

    /**
     * @param e
     * @return
     * @Description 令牌校驗異常
     */
    @ExceptionHandler(ExpireException.class)
    public RR handleExpireException(ExpireException e) {
        return RR.expire(e.getMessage());
    }
    

    /**
     * @param e
     * @return
     * @Description 無權訪問異常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public RR handleAccessDeniedException(AccessDeniedException e) {
        return RR.forbidden(e.getMessage());
    }
    

    /**
     * @param e
     * @return
     * @Description 自定義重複操作異常
     */
    public RR handleRepeatHandleException(RepeatHandleException e) {
        return RR.exception("您的請求已傳送,請勿重複操作!");
    }
    

    /**
     * @param e
     * @return
     * @Description 登入異常處理
     */
    @ExceptionHandler(AuthenticationException.class)
    public RR handleAuthenticationException(AuthenticationException e) {
        return RR.exception(e.getMessage());
    }

}

 

然後我們用Swagger來測試一下介面

1.使用者名稱為空

 

2.手機號格式錯誤

 

 

 

通過返回結果可以看到,我們的校驗註解已經幫我們按照指定的校驗方式校驗了指定的欄位屬性,我們在統一的全域性異常處理類中將提示資訊封裝成需要的返回結果就可以了。

 

分組校驗

有的童鞋在此可能有疑問了,上述方法雖然可行,但我在新增使用者和修改使用者兩個介面中,新增使用者的使用者id是自動生成的,無需校驗;修改使用者的使用者id是必傳的,則需校驗。在此業務場景中,那豈不是需要宣告兩個實體類,但這兩個實體類中的屬性又大致相同,這不是增加麻煩嗎?其實不然,在此我們可以用到groups屬性來解決此場景下的問題。

 

宣告分組

注意:在宣告分組的時候儘量加上 extend javax.validation.groups.Default 否則,在你宣告@Validated(Update.class)的時候,就會出現你在預設沒新增groups = {}的時候的校驗組@Email(message = "郵箱格式不對"),會不去校驗,因為預設的校驗組是groups = {Default.class}。

/**
 * 資料新增分組
 */
public interface Create extends Default {

}


/**
 * 資料更新分組
 */
public interface Update extends Default {

}

 

校驗註解中新增分組,groups 為一個陣列,可以新增多個分組

@NotNull(message = "id不能為空" , groups = Update.class)
private Long id;

@NotBlank(message = "使用者名稱不能為空" , groups = {Create.class, Update.class})
@Length(max = 10, message = "使用者名稱最長為10位")
private String name;

 

修改Controller中的@Validated註解,宣告校驗分組

@PermissionCheck("account:manager:add")
@PostMapping(value = "/add", produces = BaseConsts.REQUEST_HEADERS_CONTENT_TYPE)
@ApiOperation(value = "使用者管理-新增使用者介面", notes = "使用者管理-新增使用者介面", httpMethod = BaseConsts.REQUEST_METHOD, response = RR.class)
public RR add(@Validated(Create.class) @RequestBody Account account) throws Exception {
    return RR.success("新增使用者成功");
}


@PermissionCheck("account:manager:edit")
@PostMapping(value = "/edit", produces = BaseConsts.REQUEST_HEADERS_CONTENT_TYPE)
@ApiOperation(value = "使用者管理-修改使用者介面", notes = "使用者管理-修改使用者介面", httpMethod = BaseConsts.REQUEST_METHOD, response = RR.class)
public RR edit(@Validated(Update.class) @RequestBody Account account) throws Exception {
    return RR.success("編輯使用者成功");
}

 

使用相同的請求引數測試一下新增使用者和修改使用者的介面

測試新增介面

 

 

 

 

測試修改介面

 

 

 

 

 

通過介面的返回結果可以看到,新增使用者的介面並沒有校驗id這個引數了,而修改使用者的介面中則校驗了id不能為空。

 

常用校驗註解

以上就是樓主在專案中使用validation的總結歸納,下面收集了一些常用註解,紅色的標註則是樓主在專案中用的比較多的。

JSR提供的校驗註解:         
@Null   被註釋的元素必須為 null    
@NotNull    被註釋的元素必須不為 null    
@AssertTrue     被註釋的元素必須為 true    
@AssertFalse    被註釋的元素必須為 false    
@Min(value)     被註釋的元素必須是一個數字,其值必須大於等於指定的最小值    
@Max(value)     被註釋的元素必須是一個數字,其值必須小於等於指定的最大值    
@DecimalMin(value)  被註釋的元素必須是一個數字,其值必須大於等於指定的最小值    
@DecimalMax(value)  被註釋的元素必須是一個數字,其值必須小於等於指定的最大值    
@Size(max=, min=)   被註釋的元素的大小必須在指定的範圍內    
@Digits (integer, fraction)     被註釋的元素必須是一個數字,其值必須在可接受的範圍內    
@Past   被註釋的元素必須是一個過去的日期    
@Future     被註釋的元素必須是一個將來的日期    
@Pattern(regex)  被註釋的元素必須符合指定的正規表示式    


Hibernate Validator提供的校驗註解:  
@NotBlank()   驗證字串非null且非空格,長度必須大於0    
@Email  被註釋的元素必須是電子郵箱地址    
@Length(min=,max=)  被註釋的字串的大小必須在指定的範圍內    
@NotEmpty   被註釋的字串的必須非空    
@Range(min=,max=,message=)  被註釋的元素必須在合適的範圍內

參考博文:https://blog.csdn.net/u013815546/article/details/77248003/

 

相關文章