引數校驗註解

zhegeMaw發表於2024-08-16

@Valid —— Bean Validation定義

@Validated —— Spring Validation定義

分組

無分組功能

提供分組功能,可在入參驗證時,根據不同的分組採用不同的驗證機制。

巢狀驗證

用在方法入參上無法單獨提供巢狀驗證功能。

能夠用在成員屬性上,提示驗證框架進行巢狀驗證

能配合巢狀驗證註解@Valid進行巢狀驗證。

用在方法入參上無法單獨提供巢狀驗證功能。

不能用在成員屬性上,也無法提供框架進行巢狀驗證。

能配合巢狀驗證註解@Valid進行巢狀驗證。

可註解位置

可以用在方法、建構函式、方法引數和成員屬性上(兩者是否能用於成員屬性上直接影響能否提供巢狀驗證的功能)

可以用在型別、方法和方法引數上。但是不能用在成員屬性上

巢狀驗證使用@Valid,分組校驗時使用@Validated,在controller上對單個引數校驗時使用@Validated。

一、物件引數校驗

用法:哪用哪新增@Valid/@Validated

場景:POST方法,使用@RequestBody接收引數時使用

1.1 普通校驗——使用@Valid

一般場景校驗引數,使用valid註解就行,步驟如下:

1、新增校驗註解:給類中引數新增校驗註解,常用註解如下

//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=,flag=) 被註釋的元素必須符合指定的正規表示式

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

@NotNull 、@NotEmpty、 @NotBlank 3個註解的區別

  • @NotNull 適用於任何物件,不能為null (一般用這個,如Integer)
  • @NotBlank 適用於String,不為null,並且字串trim()以後length要大於0(不為“”) (String型別的用這個)
  • @NotEmpty 適用於String、Collection、Map或者陣列,不能為Null,且長度或元素個數必須大於0(可以為“”

@Pattern:註解適用於String,對於其他型別會報錯:No validator could be found for constraint 'javax.validation.constraints.Pattern' validating type 'java.lang.Integer

2、新增@Valid註解:在需要校驗的物件前新增@Valid註解即可

  • 1. 校驗對靜態變數不生效
  • 2. 對於校驗不透過的處理
    • 在使用 @Valid 註解的引數後可以緊跟著一個 BindingResult 型別的引數,用於獲取校驗結果(將校驗結果封裝在BingdingResult物件中,不會丟擲異常);
    • 校驗不透過會丟擲異常,然後全域性異常處理(推薦)

3、處理異常:全域性捕獲異常,統一處理

@ControllerAdvice               
public class BaseExceptionHandler {
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public HttpResponse handle(MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        return HttpResponse.builder().code(500).msg(fieldError.getDefaultMessage()).build();
    }
}
//這裡的MethodArgumentNotValidException是在校驗時丟擲的,實際情上因為校驗失敗還會丟擲其它異常,不過一般都是由於開發過程中導致的問題,與使用者的輸入無關,所以可以統一響應"系統異常",無需單獨處理。

@ExceptionHandler(BindingException.class) //實體物件前不加@RequestBody註解,單個物件內屬性校驗未透過丟擲的異常型別 //spring-context包裡面的異常

@ExceptionHandler(ValidationException.class) //實體物件前不加@RequestBody註解,校驗方法引數或方法返回值時,未校驗透過時丟擲的異常 //Validation-api包裡面的異常

@ExceptionHandler(MethodArgumentNotValidException.class) //實體物件前加@RequestBody註解,丟擲的異常為該類異常 //spring-context包裡面的異常

1.2 分組校驗——@Validated

1、設定分組介面

package com.datamanager.vo.request;

import javax.validation.groups.Default;

public class ValidationGroups {
    public interface Create extends Default {
    }

    public interface Update extends Default {
    }
}
//分組介面類只是普通的介面類並沒有多大意義,只是用來標識這個屬性哪種情況下被驗證

不會自動驗證預設分組:使用分組功能後,對於不指定分組的屬性,屬於預設分組,但是不會自動驗證預設分組。

解決方法:1、所有需要驗證的屬性都必須新增指定分組 2、如上所示,分組介面繼承Default介面

2、新增校驗註解:給類中引數新增校驗註解

package com.datamanager.vo.request;

import lombok.Data;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.*;

@Data
public class DatasetRequest {

    //update分組
    @NotNull(message = "id不能為空", groups = {ValidationGroups.Update.class})
    private Integer id;

    //create分組
    @NotBlank(message = "name不能為空", groups = {ValidationGroups.Create.class})
    @Size(max = 32, message = "name超出最大長度", groups = {ValidationGroups.Create.class})
    @Pattern(regexp = "^[A-Za-z0-9_-]+$", message = "name由中文、英文、下劃線和-組成", groups = {ValidationGroups.Create.class})
    private String name;

      //預設分組
    @NotBlank(message = "cnName不能為空")
    @Size(max = 20, message = "cnName超出最大長度")
    @Pattern(regexp = "^[\\u4e00-\\u9fa50-9]+$", message = "cn_name由中文和數字組成")
    private String cnName;
}

3、使用Validated註解(在需要校驗的物件前新增@Validated註解,並指定分組)

    @PutMapping("/update")
    public ResponseResult update(@Validated(ValidationGroups.Update.class) @RequestBody DatasetRequest request) {
        if (!authorityService.isAllowWrite(request.getId(), loginUser)) {
            return ResponseResult.build(ResponseCodeEnum.NO_AUTHORIZATION);
        }
        return datasetService.updateDataset(request);
    }

二、單個引數校驗

用法:類上新增@Validated,然後在需要的地方新增對應校驗註解

場景:當GET方法,使用@RequestParam接受引數時,針對單個引數分別校驗

1、controller類上新增註解@Validated

2、@RequestParam的引數前新增對應校驗註解

三、Service中對引數校驗

service類上新增註解@Validated

需要校驗的引數前加@Valid

參考資料

https://segmentfault.com/a/1190000038401180 (詳細)

https://juejin.cn/post/6844904118536912904 (簡潔 + 使用範圍)

@Validated分組校驗

使用@Validated分組遇到的坑

相關文章