SpringBoot參數(shù)校驗(yàn)的方法總結(jié)
一、前言
在上一篇MyBatis-plus 初體驗(yàn) 中已經(jīng)簡單實(shí)現(xiàn)了 MyBatis-Plus 數(shù)據(jù)庫查詢。我們知道 CURD 離不開前后端的數(shù)據(jù)交互,因此參數(shù)校驗(yàn)是必不可少的。這篇主要講一下 SpringBoot 參數(shù)校驗(yàn)。
在 Web 開發(fā)中經(jīng)常需要對前端傳過來的參數(shù)進(jìn)行校驗(yàn),例如格式校驗(yàn)、非空校驗(yàn)等,基本上每個接口都需要進(jìn)行校驗(yàn)。如果使用常規(guī)的 IF ELSE 進(jìn)行校驗(yàn),隨著參數(shù)越來越多,校驗(yàn)邏輯的冗余度也越來越高,導(dǎo)致維護(hù)性變差。在 Java 中定義了一套基于注解的數(shù)據(jù)校驗(yàn)規(guī)范 Bean Validation ,通過一些簡單的注解就能完成必要的邏輯校驗(yàn),相對來說就方便了很多。而 Bean Validation 只是規(guī)范,并沒有具體的實(shí)現(xiàn),Hibernate 提供了具體的實(shí)現(xiàn),也即 Hibernate Validator ,這個也是目前使用得比較多的驗(yàn)證器了。
二、注解介紹
validator 內(nèi)置注解
@Null被注釋的元素必須為null@NotNull被注釋的元素必須不為null@AssertTrue被注釋的元素必須為true@AssertFalse被注釋的元素必須為false@Min(value)被注釋的元素必須是一個數(shù)字,其值必須大于等于指定的最小值@Max(value)被注釋的元素必須是一個數(shù)字,其值必須小于等于指定的最大值@DecimalMin(value)被注釋的元素必須是一個數(shù)字,其值必須大于等于指定的最小值@DecimalMax(value)被注釋的元素必須是一個數(shù)字,其值必須小于等于指定的最大值@Size(max, min)被注釋的元素的大小必須在指定的范圍內(nèi)@Digits (integer, fraction)被注釋的元素必須是一個數(shù)字,其值必須在可接受的范圍內(nèi)@Past被注釋的元素必須是一個過去的日期@Future被注釋的元素必須是一個將來的日期@Pattern(value)被注釋的元素必須符合指定的正則表達(dá)式
Hibernate Validator 附加的 constraint
@Email被注釋的元素必須是電子郵箱地址@Length被注釋的字符串的大小必須在指定的范圍內(nèi)@NotEmpty被注釋的字符串的必須非空@Range被注釋的元素必須在合適的范圍內(nèi)@NotBlank驗(yàn)證字符串非null,且長度必須大于0
注意
@NotNull用于驗(yàn)證對象是否不為null,無法檢測長度為0的字符串;@NotEmpty用于 String、Map 或者數(shù)組等集合類型不能為null且長度必須大于0;@NotBlank只能用于String,不能為null,且調(diào)用trim()后,長度必須大于0;
校驗(yàn)字符串是否為空,使用
@NotNull,只有參數(shù)不傳的時候才會檢測到,傳了空值(例如空字符串)仍然可以通過校驗(yàn),因此應(yīng)該使用@NotBlank
三、添加依賴
在 SpringBoot 中 Bean Validation 已經(jīng)集成在 starter-web 中,因此無需再添加依賴。但是本人實(shí)際測試發(fā)現(xiàn),直接使用好像不行,因此添加了如下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
四、創(chuàng)建用于校驗(yàn)的實(shí)體類
創(chuàng)建一個 validator 目錄,在里面創(chuàng)建一個 UserDTO 類:

本來想直接復(fù)用之前創(chuàng)建的 entity 類,但是后來想了下,entity 用于建立數(shù)據(jù)庫的映射關(guān)系,字段跟數(shù)據(jù)表是一一對應(yīng)的,而這里的 validator 是用于校驗(yàn)前端傳過來的參數(shù),字段跟前端傳的參數(shù)是對應(yīng)的,因此不能復(fù)用,需要單獨(dú)寫一個 validator 類。
順便提一下,在 RestController 中使用自己定義的對象,需要有 setter、getter 之類的方法,或者使用 lombok 的 @Data 注解。如果不加的話會報錯:
No converter found for return value of type: class validator.UserDTO
使用 getter、setter 方法如下:
public class UserDTO {
private String username;
private Integer age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
使用 lombok 的 @Data 注解如下,代碼與上面等效:
@Data
public class UserDTO {
private String username;
private Integer age;
}
五、寫一個測試用的接口
添加一個 POST 接口,從請求體中獲取參數(shù),然后原封不動返回過去(主要是用來測試參數(shù)校驗(yàn)的,這里接口邏輯并不重要)
@PostMapping("validateUser")
public UserDTO userValidate(@RequestBody UserDTO userDTO) {
return userDTO;
}
六、在實(shí)體類中添加注解
給需要校驗(yàn)的參數(shù)添加注解:
@Data
public class UserDTO {
@NotBlank(message = "用戶名不能為空")
private String username;
@NotBlank(message = "手機(jī)號不能為空")
private String mobile;
@NotNull(message = "性別不能為空")
private Integer sex;
@NotNull(message = "年齡不能為空")
private Integer age;
@NotBlank(message = "郵箱不能為空")
@Email(message = "郵箱格式錯誤")
private String email;
}
七、在 controller 方法中添加 Validated 注解
然后需要在 controller 方法體添加 @Validated ,不加 @Validated 校驗(yàn)會不起作用。

用下面的數(shù)據(jù)測試一下:
{
"username": "",
"mobile": "2333",
"sex": 0,
"age": 0,
"email": "233@dby.com"
}
可以看到校驗(yàn)是成功了,但是后臺拋了一個異常:
Validation failed for argument [0] in public validator.UserDTO com.hhlnyfz.hhlnyfz.HelloController.userValidate(validator.UserDTO): [Field error in object ‘userDTO' on field ‘username': rejected value []; codes [NotBlank.userDTO.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userDTO.username,username]; arguments []; default message [username]]; default message [用戶名不能為空]] ]
然后返回參數(shù)并不理想,前端也并不容易處理返回參數(shù):

八、添加全局異常處理
上面這種情況需要添加一下全局異常處理,這樣比較規(guī)范。創(chuàng)建一個 GlobalExceptionHandler 類,在類的上面添加 @RestControllerAdvice 注解,然后添加如下代碼:
/**
* 全局異常處理類
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
// 捕獲 MethodArgumentNotValidException 異常
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public HashMap<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
HashMap<String, Object> map = new HashMap<>();
map.put("code", 400);
map.put("msg", e.getMessage());
map.put("url", request.getRequestURL());
return map;
}
// 其他異常處理方法
}
這邊 @ExceptionHandler 注解中的 MethodArgumentNotValidException.class 用于捕獲請求參數(shù)異常。如果是 Exception.class 表示捕獲全部異常。不要用一個方法處理所有的異常,而是一個方法處理一種異常。如果需要處理其他異常,可以在下面添加方法。
還是用剛才的測試用例,這次異常被捕獲到了,返回的內(nèi)容如下:

可以看到 e.getMessage() 把整個錯誤堆棧信息全部打印出來了,但我們只需要把最后的 default message 返回給前端就行,因此改用 e.getBindingResult().getFieldError().getDefaultMessage() ,然后 IDE 給了提示:
Method invocation ‘getDefaultMessage' may produce ‘NullPointerException'
也就是說 e.getBindingResult().getFieldError() 可能會是一個空指針 null ,于是按照 IDE 的提示用 Objects.requireNonNull 包裹一下,最終代碼如下:
/**
* 全局異常處理類
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
// 捕獲 MethodArgumentNotValidException 異常
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public HashMap<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
HashMap<String, Object> map = new HashMap<>();
map.put("code", 400);
map.put("msg", Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
map.put("url", request.getRequestURL());
return map;
}
// 其他異常處理方法
}
響應(yīng)內(nèi)容如下:

當(dāng)然這邊還是有不規(guī)范的地方,沒有用統(tǒng)一響應(yīng)體進(jìn)行返回,后面會介紹如何封裝統(tǒng)一響應(yīng)體。
九、分組校驗(yàn)
@Valid 和 @Validated 兩個注解都可以實(shí)現(xiàn)校驗(yàn),前面的功能用 @Valid 也是可以的,但是 @Validated 功能更強(qiáng)大,可以實(shí)現(xiàn)分組校驗(yàn)。什么是分組校驗(yàn),分組校驗(yàn)實(shí)際上實(shí)現(xiàn)了實(shí)體類的復(fù)用,有時候并不希望對所有的參數(shù)都進(jìn)行校驗(yàn),例如下面這個情況:
@Data
public class Route {
@NotNull(message = "始發(fā)地省id不能為空")
private Integer startProvinceId;
@NotNull(message = "目的地省id不能為空")
private Integer endProvinceId;
@NotBlank(message = "詳細(xì)地址不能為空")
private String address;
}
假如在一個接口中只希望校驗(yàn) startProvinceId 和 address ,而在另一個接口中只希望校驗(yàn) endProvinceId 和 address ,這個時候就可以用分組校驗(yàn)??梢远x一個接口:
public interface ValidateGroup {
interface RouteValidStart {}
interface RouteValidEnd {}
}
然后在實(shí)體類中添加分組:
@Data
public class Route {
@NotNull(groups = {RouteValidStart.class}, message = "始發(fā)地省id不能為空")
private Integer startProvinceId;
@NotNull(groups = {RouteValidEnd.class}, message = "目的地省id不能為空")
private Integer endProvinceId;
@NotBlank(groups = {RouteValidStart.class, RouteValidEnd.class}, message = "詳細(xì)地址不能為空")
private String address;
}
然后在校驗(yàn)的時候只需要把分組傳入 @Validate 就可以實(shí)現(xiàn)指定參數(shù)的校驗(yàn):
@RequestMapping("addRoute")
public ServerResponse addRoute(@RequestBody @Validated({RouteValidStart.class}) Route route) {
// ...
return ServerResponse.success();
}
十、單個參數(shù)校驗(yàn)
在參數(shù)前面加上注解即可:
@PostMapping("/get")
public ReturnVO getUserInfo(@RequestParam("userId") @NotNull(message = "用戶ID不能為空") String userId){
return new ReturnVO().success();
}
然后在 Controller 類上面增加 @Validated 注解,注意不是增加在參數(shù)前面。
到此這篇關(guān)于SpringBoot參數(shù)校驗(yàn)的方法總結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot參數(shù)校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java與Spring?boot后端項(xiàng)目Bug超全總結(jié)
Spring Boot是一個開源的 Java 開發(fā)框架,它的目的是簡化Spring應(yīng)用程序的開發(fā)和部署,下面這篇文章主要給大家介紹了關(guān)于Java與Spring?boot后端項(xiàng)目Bug的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06
Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼
這篇文章主要介紹了Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
IntelliJ IDEA2021.1 配置大全(超詳細(xì)教程)
這篇文章主要介紹了IntelliJ IDEA2021.1 配置大全(超詳細(xì)教程),需要的朋友可以參考下2021-04-04
springboot實(shí)現(xiàn)string轉(zhuǎn)json json里面帶數(shù)組
這篇文章主要介紹了springboot實(shí)現(xiàn)string轉(zhuǎn)json json里面帶數(shù)組,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
Java結(jié)構(gòu)型設(shè)計(jì)模式中建造者模式示例詳解
建造者模式,是一種對象構(gòu)建模式 它可以將復(fù)雜對象的建造過程抽象出來,使這個抽象過程的不同實(shí)現(xiàn)方法可以構(gòu)造出不同表現(xiàn)的對象。本文將通過示例講解建造者模式,需要的可以參考一下2022-09-09
springMVC發(fā)送郵件的簡單實(shí)現(xiàn)
本篇文章主要介紹了springMVC發(fā)送郵件的簡單實(shí)現(xiàn) ,主要是利用利用javax.mail發(fā)送郵件,圖片與附件都可發(fā)送,有興趣的可以了解一下2017-04-04
聊聊Controller中RequestMapping的作用
這篇文章主要介紹了Controller中RequestMapping的作用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02

