Validation - Spring Boot項目中參數檢驗的利器
什么是Validation
Sping Boot官方原文:
When it comes to validating user input, Spring Boot provides strong support for this common, yet critical, task straight out of the box.
Although Spring Boot supports seamless integration with custom validators, the de-facto standard for performing validation is Hibernate Validator, the Bean Validation framework’s reference implementation.
Validation是Spring Boot官方提供的一個參數檢驗的工具,通常負責驗證用戶輸入,也就是在從前端獲取請求時對請求中包含的信息參數進行檢驗,避免了進入后端檢驗這個流程的耗時與邏輯編寫
為什么要使用Validation
Validation在Spring Boot架構的分布式項目當中比較常見,其優點我總結為:
- 與后端邏輯分離,做到了解耦
- 使用簡單,只需要在需要進行參數檢驗的參數(DTO層)上增加一個注釋即可,不需要編寫大量接口和實現代碼
- 官方支持,使用穩定
接下來我們將通過前兩個角度來看看Validation是怎樣簡單上手使用的,我將使用我的學習項目作為示例
如何使用Validation
在一種情況下,比如我們設計數據庫表的時候,往往會有一些字段包含實際意義,比如郵箱或電話號碼,這些字段與分配的隨機id不同,在現實中有一定的規范;另一種情況下,比如我們的用戶設置初始密碼時,我們也通常會要求用戶至少輸入6位,有時還會要求至少有一位大寫字母等,這時我們就需要對用戶輸入的數據進行檢驗,檢查這些數據是否符合實際規范或者我們的要求,此時就可以用上Validation
比如在我的項目中,作為一個電商平臺,有用戶表如下:
public class User {private Long id;private String username;private String password;private String phone;private String email;private Date registerTime;private Integer status;private String realName;private String address;
}
電商平臺會出現用戶注冊的場景,于是我們就要定義用戶注冊時返回的數據的DTO類
public class UserRegisterDTO {@NotBlank(message = "用戶名不能為空")private String username;@NotBlank(message = "密碼不能為空")@Size(message = "密碼長度至少為6", min = 6)private String password;@NotBlank(message = "電話號碼不能為空")@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手機號格式不正確")private String phone;@NotBlank(message = "郵箱不能為空")@Email(message = "郵箱格式不正確")private String email;
}
在注冊時,我們就需要規范用戶輸入的這些信息,所有信息都是必要的不能為空,同時密碼有長度限制,電話號碼有其固定格式,郵箱同理,我們在這里就可以通過Validation的注釋來告訴系統,在把用戶返回的這些數據包裝成對應的DTO類時,需要進行檢查
再具體一點,我們怎么通知系統呢
首先我們需要添加依賴,比如我的項目中:
<dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
我做的是一個微服務項目,目前上述的代碼都位于用戶模塊,因此這個依賴我也是添加在我用戶模塊的子pom中,有了這個依賴,我們就可以開始使用Validation
接著我們需要在對應的API中聲明,告訴系統我們在處理這個請求之前需要對其參數進行檢驗,比如我注冊的Restful API:
// 注冊用戶@PostMapping("/register")public Result<Boolean> register(@Valid @RequestBody UserRegisterDTO dto) {return userService.register(dto) ?Result.ok(true) :Result.fail("Register Failed");}
只有在這里指明了@Valid這個注釋,系統才會對其進行Validation相關處理
接著我們就要進一步對參數進行注釋,正如我之前給出的代碼,在DTO層對應的參數前添加相應的注釋即可,一般來說,validation注釋有以下幾種:
- @AssertFalse:被注解的元素必須為false
- @AssertTrue:被注解的元素必須為true
- @DecimalMax(value):被注解的元素必須是一個數字,其值必須小于等于指定的最大值
- @DecimalMin(value):被注解的元素必須是一個數字,其值必須大于等于指定的最小值
- @Digits(integer, fraction):被注解的元素必須是一個數字,其值必須在可接受的范圍內
- @Future:被注解的元素必須是一個將來的日期
- @Max(value):被注解的元素必須是一個數字,其值必須小于等于指定的最大值
- @Min(value):被注解的元素必須是一個數字,其值必須大于等于指定的最小值
- @NotNull:被注解的元素必須不為null
- @Null:被注解的元素必須為null
- @Past:被注解的元素必須是一個過去的日期
- @Pattern(regex, flag):被注解的元素必須符合指定的正則表達式
- @Size(min, max):被注解的元素的大小必須在指定的范圍內
- @Email:被注解的元素必須是電子郵件地址
- @NotEmpty:被注解的元素不能為null,并且長度必須大于0
- @NotBlank:被注解的元素為字符串,并且被trim()以后length要大于0
- @Range(min, max):被注解的元素必須在合適的范圍內
- @URL:被注解的元素必須是一個有效的URL
在元素之前添加相應的注釋,項目啟動后,把用戶返回的參數包裝成DTO類對象時就會進行檢查
但是只檢查出來了,最多也就會返回400的錯誤代碼,前端只是了解到有錯誤,但是無法對錯誤進行定位,于是此時我們就需要進一步處理
自定義Result類,用來統一管理前端請求的處理結果
public class Result<T> {private Integer code;private String msg;private T data;public static <T> Result<T> ok(T data) {Result<T> r = new Result<>();r.setCode(200);r.setMsg("success");r.setData(data);return r;}public static <T> Result<T> fail(String msg) {Result<T> r = new Result<>();r.setCode(500);r.setMsg(msg);return r;}
}
自定義異常處理類,用于對運行時錯誤進行處理
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public Result<?> handleValidationException(MethodArgumentNotValidException ex) {String errorMsg = ex.getBindingResult().getFieldError().getDefaultMessage();return Result.fail(errorMsg);}
}
有了這兩段代碼,項目啟動后就有了自定義的對運行時錯誤進行定位處理的能力,比如用戶注冊時輸入的初始密碼小于6位,通過Validation注釋檢查出了這一錯誤,然后匯報給GlobalExceptionHandler類,它就將錯誤信息包裝,并最終包裝成Result類,返回給前端,這樣前端就能看見具體的錯誤信息,比如我測試如下:
我在Postman中進行用戶注冊測試,這是注冊的body json部分:
{"username": "testValid6","password": "five","email": "12345678","phone": "12345678"
}
最后返回了結果:
{"timestamp": "2025-07-19T04:59:28.091+00:00","status": 400,"error": "Bad Request","trace": 略"message": "Validation failed for object='userRegisterDTO'. Error count: 3","errors": [{"codes": ["Email.userRegisterDTO.email","Email.email","Email.java.lang.String","Email"],"arguments": [{"codes": ["userRegisterDTO.email","email"],"arguments": null,"defaultMessage": "email","code": "email"},[],{"arguments": null,"defaultMessage": ".*","codes": [".*"]}],"defaultMessage": "郵箱格式不正確","objectName": "userRegisterDTO","field": "email","rejectedValue": "12345678","bindingFailure": false,"code": "Email"},{"codes": ["Pattern.userRegisterDTO.phone","Pattern.phone","Pattern.java.lang.String","Pattern"],"arguments": [{"codes": ["userRegisterDTO.phone","phone"],"arguments": null,"defaultMessage": "phone","code": "phone"},[],{"arguments": null,"defaultMessage": "^1[3-9]\\d{9}$","codes": ["^1[3-9]\\d{9}$"]}],"defaultMessage": "手機號格式不正確","objectName": "userRegisterDTO","field": "phone","rejectedValue": "12345678","bindingFailure": false,"code": "Pattern"},{"codes": ["Size.userRegisterDTO.password","Size.password","Size.java.lang.String","Size"],"arguments": [{"codes": ["userRegisterDTO.password","password"],"arguments": null,"defaultMessage": "password","code": "password"},2147483647,6],"defaultMessage": "密碼長度至少為6","objectName": "userRegisterDTO","field": "password","rejectedValue": "five","bindingFailure": false,"code": "Size"}],"path": "/user/register"
}
可以看出,這里按照DTO設計的參數的順序,通通給出了錯誤的信息,這樣就能夠精準地定位錯誤,前端也就可以對其做相應的處理
總結
整體來說,Validation將原先需要一個API一個API編寫的參數檢驗邏輯簡化至如此方便,可以說是Spring Boot項目中官方提供的一個參數檢驗的利器了,對參數標注只需要注釋,錯誤處理可以統一編寫錯誤處理類,前端也可以清晰地看見具體的錯誤,能夠極大地簡化這部分邏輯的開發,省去繁瑣的重復性工作