SpringMVC實現引數校驗

Evan1024發表於2024-03-02

在 Web 應用的三層架構中,確保在表述層(Presentation Layer)對資料進行檢查和校驗是非常重要的。正確的資料校驗可以確保業務邏輯層(Business Logic Layer)基於有效和合法的資料進行處理,同時將錯誤的資料隔離在業務邏輯層之外。這有助於提高系統的健壯性、安全性和可維護性。

校驗概述

JSR-303是Java EE 6中的一項子規範,也被稱為Bean Validation。這個規範主要用於對Java Bean中的欄位值進行驗證。JSR-303提供了一套註解和API,方便開發者在實體類中對欄位進行驗證,以確保資料的合法性。

在JSR-303中,內建了許多常用的約束規則,如@NotNull(非空驗證)、@Size(長度驗證)、@Range(範圍驗證)等。這些註解可以直接應用於實體類的欄位上,以宣告驗證規則。同時,JSR-303還提供了驗證器(Validator)介面,用於執行驗證操作。開發者可以透過注入Validator例項,或者使用Validation工具類來觸發驗證。

在Spring MVC中,JSR-303得到了很好的支援。開發者可以在控制器的方法引數上使用JSR-303的註解,以實現表單資料的自動驗證。如果引數不滿足約束條件,Spring MVC會丟擲MethodArgumentNotValidException異常,並將錯誤資訊繫結到相應的欄位上。

常用註解

@NotNull:確保欄位值不為null。
@NotBlank:用於String型別引數校驗,檢查字串不能為null且去除首尾空格後長度大於0。
@NotEmpty:主要用於集合校驗,校驗集合不能為null且size大於0,也可以用於String的校驗,確保字
@Null:確保欄位值為null。
@AssertTrue 和 @AssertFalse:用於boolean欄位,確保欄位值為true或false。
@Min 和 @Max:用於Number和String欄位,確保欄位值在指定的範圍內。
@DecimalMin 和 @DecimalMax:用於BigDecimal,BigInteger,String,byte,short,int,long欄位,確保欄位值在指定的小數範圍內。需要注意的是,小數驗證存在精度問題。
@Size:用於Collection,Map,Array,String欄位,確保欄位的長度(或大小)在指定的範圍內。
@Digits:用於BigDecimal,BigInteger,String,byte,short,int,long欄位,驗證值的數字構成是否合法。它有兩個引數,integer表示整數部分的數字的位數,fraction表示小數部分的數字的位數。
@Email:用於String欄位,驗證欄位值是否為有效的電子郵件地址。
@Past 和 @Future:用於日期和時間欄位,確保日期在過去或未來。
@Pattern:用於String欄位,驗證欄位值是否匹配指定的正規表示式。
@Range:控制一個數值的範圍,如數字、日期等。
@Valid:遞迴地對關聯物件進行校驗,如果關聯物件是個集合或者陣列,那麼對其中的元素進行遞迴校驗;如果是一個map,則對其中的值部分進行校驗。
@CreditCardNumber:信用卡驗證。
@URL:驗證一個字串是否符合URL格式。
@ScriptAssert:使用指令碼語言進行自定義驗證。

@NotNull、@NotEmpty和@NotBlank註解說明

@NotNull、@NotEmpty和@NotBlank都是Java中的註解,用於在執行時檢查屬性值是否符合特定條件。它們的使用場景如下:

@NotNull:這個註解用於基本資料型別和它們的包裝類(如Integer、Long、Double等),以及String型別。當它被用在String型別上時,表示該資料不能為null,但可以為空字串。這個註解一般用在基本資料型別的非空校驗上,如果被其標註的欄位使用了@Size、@Max或@Min等註解,那麼還可以對欄位的數值大小進行控制。此外,@NonNull是JSR 305(缺陷檢查框架)的註解,用於方法或建構函式的引數上,表明所修飾的引數不能為null。它會在程式碼檢查(靜態檢查)時給出一個風險警告,但執行時不會報錯。
@NotEmpty:這個註解用於String、Collection集合、Map、陣列等型別。加了@NotEmpty註解的引數不能為null,而且長度或大小必須大於0。這個註解一般用在集合類上。

@NotBlank:這個註解只能用於String型別。加了@NotBlank註解的字串不能為null,而且去除兩端空白字元後的長度(trimmed length)必須大於0。這個註解通常用於前端傳送過來的資料,先進行校驗處理,再進行邏輯判斷。

總結來說,@NotNull、@NotEmpty和@NotBlank的使用場景主要取決於你想要驗證的資料型別和約束條件。@NotNull主要用於基本資料型別和String的非空檢查,@NotEmpty主要用於集合類,而@NotBlank則主要用於String型別,要求字串不為null且去除空白字元後的長度大於0。在實際開發中,一定要根據具體需求選擇合適的註解,否則可能會導致驗證邏輯出錯。

操作演示

1.匯入依賴

<!--validation-api介面的實現-->
<dependency>  
    <groupId>org.hibernate.validator</groupId>  
    <artifactId>hibernate-validator</artifactId>  
    <version>6.2.5.Final</version>  
</dependency>  
<!--Java Bean Validation 的規範介面-->
<dependency>  
    <groupId>javax.validation</groupId>  
    <artifactId>validation-api</artifactId>  
    <version>2.0.1.Final</version>  
</dependency>
<!--Java 註解處理器,它用於在編譯時檢查 JSR-303 註解的正確性和一致性-->
<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator-annotation-processor</artifactId>
	<version>6.2.5.Final</version>
</dependency>

2.校驗實體物件引數
實體類

@Data
public class User {

    @NotBlank
    private String name;
    @NotNull(message = "年齡最小1歲,最大100歲")
    @Range(min = 1 , max = 100)
    private Integer age;
    @NotNull(message = "郵箱不能為空,並且要符合規範")
    @Email
    private String email;
    @NotNull
    @Size(min = 2,max = 50)
    private String address;
    @Past
    private Date birthday;
    @NotEmpty
    private List<String> users;
}

controller

//實體物件校驗
//在實體類引數和 BindingResult 之間不能有任何其他引數, BindingResult可以接受錯誤資訊,避免資訊丟擲!
//@Validated 或 @Valid 代表應用校驗註解!
@PostMapping("validateObject")
private String validateObject(@Validated @RequestBody User user, BindingResult result, Model model) {
	if (result.hasErrors()) {
		String errorMsg = Objects.requireNonNull(result.getFieldError()).toString();
		System.out.println(errorMsg);
		model.addAttribute("msg",errorMsg);
		return "error";
	}
	System.out.println("user=" + user);
	model.addAttribute("msg",user);
	return "success";
}

說明:
@Validated註解是SpringMVC提供的。
@Valid註解是JSR-303提供的。

3.基本型別校驗

    //基本型別校驗
@GetMapping("validate")
public String validateBasic(@RequestBody @Min(18) Integer age, @RequestBody @NotBlank String name) {
    return "success";
}

配置校驗器

說明:一般情況下不需要配置,SpringMVC已經配置好預設校驗器

基於xml配置方式
LocalValidatorFactoryBean 是 Spring 框架提供的一個類,它實現了 ValidatorFactory 介面。在 Spring 環境中,LocalValidatorFactoryBean 通常被用作 Bean Validation 的預設實現。Spring 的自動配置通常會為你建立一個 LocalValidatorFactoryBean 例項,並將其配置為應用的預設 ValidatorFactory。這意味著,當你在 Spring 控制器或服務中使用 @Valid 或 @Validated 註解時,背後實際上是 LocalValidatorFactoryBean 在執行驗證邏輯。

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<mvc:annotation-driven validator="validator"/>

基於配置類方式
構建 ValidatorFactory 例項。
ValidatorFactory 是一個工廠類,用於生產 Validator 例項,這些 Validator 例項用於執行實際的驗證邏輯。當你需要在非 Spring 環境中進行 Bean Validation,或者當 Spring 的自動配置不能滿足你的需求時,你可以使用這個方法手動配置和獲取 Validator。

@Configuration  
@EnableWebMvc  
public class WebConfig implements WebMvcConfigurer {  
  
    @Bean  
    public Validator validator() {  
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();  
        return factory.getValidator();  
    }  
}

結論:
Validation.buildDefaultValidatorFactory():這是 Bean Validation API 的標準方法,用於在非 Spring 環境中建立 ValidatorFactory。在 Spring 中,除非你有特殊需求需要繞過 Spring 的自動配置,否則通常不會直接使用這個方法。
LocalValidatorFactoryBean:這是 Spring 框架特有的類,用於在 Spring 應用中作為預設的 ValidatorFactory。在 Spring 應用中,你通常不需要直接操作 LocalValidatorFactoryBean,因為 Spring 會自動為你配置和注入它。但是,如果你需要自定義驗證器的配置(例如,更改訊息插值器、遍歷提供者等),你可以建立一個 LocalValidatorFactoryBean 的 Bean 並進行相應的配置。

4.集合校驗

//集合校驗
@PostMapping("validateList")
public Object validateList(@Valid @RequestBody List<@Positive User> users, BindingResult result) {
	if (result.hasErrors()) {
		Map<String,Object> map = new HashMap<>();
		map.put("code","400");
		map.put("msg","引數校驗失敗,不符合規則");
		return map;
	}
	System.out.println(users);
	return users;
}

自定義校驗註解

1.定義一個註解類,並使用@interface關鍵字。
2.在註解類上新增@Constraint註解,並指定校驗器類。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyCustomValidator.class)
public @interface MyCustomValidation {

    String message() default "該欄位不能為空";

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

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

3.建立一個實現ConstraintValidator介面的校驗器類,實現校驗邏輯。

public class MyCustomValidator implements ConstraintValidator<MyCustomValidation,String> {

    @Override
    public void initialize(MyCustomValidation constraintAnnotation) {
        //可以在這裡進行一些初始化操作
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 自定義校驗邏輯:檢查字串是否為空或僅包含空白字元
        return value != null && !value.trim().isEmpty();
    }
}

4.在需要校驗的欄位上應用自定義註解。

public class CustomValidationEntity {

    @MyCustomValidation
    private String customField;
}

全域性異常校驗配置

為了優雅地處理校驗失敗的情況,你可以實現一個全域性異常處理器來捕獲MethodArgumentNotValidException異常,並從中提取錯誤資訊。這個異常是如果控制器方法形參不滿足約束條件,Spring MVC會丟擲MethodArgumentNotValidException異常,並將錯誤資訊繫結到相應的欄位上。例如:

@ControllerAdvice  
public class GlobalExceptionHandler {  
  
    @ExceptionHandler(MethodArgumentNotValidException.class)  
    public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) {  
        BindingResult bindingResult = ex.getBindingResult();  
        Map<String, String> errors = new HashMap<>();  
        for (FieldError fieldError : bindingResult.getFieldErrors()) {  
            errors.put(fieldError.getField(), fieldError.getDefaultMessage());  
        }  
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);  
    }  
}

在這個例子中,GlobalExceptionHandler類使用@ControllerAdvice註解標記為一個全域性異常處理器。handleValidationExceptions方法則用於處理MethodArgumentNotValidException異常。它從BindingResult中提取錯誤資訊,並將這些資訊作為響應返回給客戶端。

這樣,當客戶端傳送一個包含無效資料的請求時,伺服器會返回一個包含錯誤資訊的響應,而不是直接丟擲異常。

相關文章