在 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中提取錯誤資訊,並將這些資訊作為響應返回給客戶端。
這樣,當客戶端傳送一個包含無效資料的請求時,伺服器會返回一個包含錯誤資訊的響應,而不是直接丟擲異常。