SpringBoot2.x 參數(shù)校驗(yàn)問題小結(jié)
本文主要對SpringBoot2.x參數(shù)校驗(yàn)進(jìn)行簡單總結(jié),其中SpringBoot使用的2.4.5版本。
一、引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
二、實(shí)體類
User類:
package com.rtxtitanv.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.*;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.model.User
* @description 用戶實(shí)體類
* @date 2021/8/15 16:28
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
@NotNull(message = "id不能為空")
private Long id;
@Length(min = 6, max = 20, message = "用戶名長度不小于6,不超過20")
@NotNull(message = "用戶名不能為空")
private String username;
@Pattern(regexp = "^[A-Z][A-Za-z0-9_]{5,19}$", message = "密碼以大寫英文字母開頭,只包含英文字母、數(shù)字、下劃線,長度在6到20之間")
@NotNull(message = "密碼不能為空")
private String password;
@Max(value = 60, message = "年齡最大為60")
@Min(value = 18, message = "年齡最小為18")
@NotNull(message = "年齡不能為空")
private Integer age;
@Email(message = "郵箱格式不正確")
@NotEmpty(message = "郵箱不能為空")
private String email;
private String rank;
}
通用響應(yīng)類:
package com.rtxtitanv.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;
import java.io.Serializable;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.model.CommonResult
* @description 通用響應(yīng)類
* @date 2021/8/15 17:35
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class CommonResult<T> implements Serializable {
private static final long serialVersionUID = 5231430760082814286L;
private int code;
private String message;
private T data;
public static <T> CommonResult<T> ok(String message, T data) {
return new CommonResult<T>(HttpStatus.OK.value(), message, data);
}
public static <T> CommonResult<T> fail(int code, String message, T data) {
return new CommonResult<>(code, message, data);
}
}
三、常用的校驗(yàn)注解
這里對一些用于參數(shù)校驗(yàn)的常用注解進(jìn)行總結(jié):
@Null:必須為null。@NotNull:必須不為null。@AssertTrue:必須為true。@AssertFalse:必須為false。@Min(value):必須是一個大于等于指定值的數(shù)字。@Max(value):必須是一個小于等于指定值的數(shù)字。@DecimalMin(value):必須是一個大于等于指定值的數(shù)字。@DecimalMax(value):必須是一個小于等于指定值的數(shù)字。@Size(min=,max=):大小必須在指定的范圍內(nèi)。@Digits(integer, fraction):必須是一個數(shù)字,其值必須在可接受的范圍內(nèi)。@Past:必須是一個過去的日期。@Future:必須是一個將來的日期。@Pattern(regex=):必須符合指定的正則表達(dá)式。@Email:必須是一個有效的email地址。@Length(min=,max=):字符串長度是否在指定范圍內(nèi)。@NotBlank:必須非空且長度大于0。@NotEmpty:必須不為null或空。@URL(protocol=,host,port):必須是一個有效的URL,如果提供了protocol,host等,則還需滿足提供的條件。
四、校驗(yàn)Controller中的參數(shù)
1.校驗(yàn)請求體
創(chuàng)建UserController,在需要校驗(yàn)的參數(shù)上添加@Valid注解:
package com.rtxtitanv.controller;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.controller.UserController
* @description UserController
* @date 2021/8/15 16:33
*/
@RequestMapping("/user")
@RestController
public class UserController {
@PostMapping("/save")
public CommonResult<User> saveUser(@RequestBody @Valid User user) {
return CommonResult.ok("保存用戶成功", user);
}
}
啟動項(xiàng)目,發(fā)送如下POST請求,請求地址為http://localhost:8080/user/save:

如果驗(yàn)證失敗會拋出MethodArgumentNotValidException,默認(rèn)情況下,Spring會將MethodArgumentNotValidException異常轉(zhuǎn)換為HTTP Status 400。查看控制臺打印的日志發(fā)現(xiàn)拋出了MethodArgumentNotValidException:

創(chuàng)建全局異常處理類捕獲異常并進(jìn)行處理:
package com.rtxtitanv.handler;
import com.rtxtitanv.model.CommonResult;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.handler.GlobalExceptionHandler
* @description 全局異常處理類
* @date 2021/8/15 16:36
*/
@RestControllerAdvice(annotations = {Controller.class, RestController.class})
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
public CommonResult<Map<String, String>> validateException(MethodArgumentNotValidException e) {
Map<String, String> errors = new HashMap<>(16);
e.getBindingResult().getAllErrors()
.forEach(error -> errors.put(((FieldError)error).getField(), error.getDefaultMessage()));
return CommonResult.fail(HttpStatus.BAD_REQUEST.value(), "無效的參數(shù)", errors);
}
}
重啟項(xiàng)目后再次發(fā)送如下POST請求:

2.校驗(yàn)請求參數(shù)
UserController上添加Validated注解并新增以下方法:
@GetMapping("/get/{id}")
public CommonResult<User>
getUserById(@Valid @PathVariable(value = "id") @Min(value = 1, message = "id不能小于1") Long id) {
User user = new User(id, "ZhaoYun", "A123456sd", 20, "zhaoyun123@xxx.com", "黃金");
return CommonResult.ok("根據(jù)id查詢用戶成功", user);
}
@DeleteMapping("/delete")
public CommonResult<User> deleteByUsername(
@Valid @RequestParam(value = "username") @Size(min = 6, max = 20, message = "用戶名長度不在指定范圍內(nèi)") String username) {
User user = new User(1L, username, "A123456sd", 20, "zhaoyun123@xxx.com", "黃金");
return CommonResult.ok("根據(jù)用戶名刪除用戶成功", user);
}
全局異常處理類中新增以下方法:
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
public CommonResult<Map<String, String>> handleConstraintViolationException(ConstraintViolationException e) {
Map<String, String> errors = new HashMap<>(16);
e.getConstraintViolations().forEach(constraintViolation -> errors
.put(constraintViolation.getPropertyPath().toString(), constraintViolation.getMessage()));
return CommonResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "無效的參數(shù)", errors);
}
發(fā)送如下GET請求,請求地址為http://localhost:8080/user/get/0:

發(fā)送如下DELETE請求,請求地址為http://localhost:8080/user/delete?username=ZhaoZiLong_1896582826:

五、校驗(yàn)Service中的參數(shù)
通過@Validated和@Valid注解組合使用不僅可以校驗(yàn)Controller中的參數(shù),還可以校驗(yàn)任何Spring組件中的參數(shù)。
UserService:
package com.rtxtitanv.service;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.service.UserService
* @description UserService
* @date 2021/8/15 16:46
*/
@Validated
public interface UserService {
/**
* 更新用戶
*
* @param user 用戶參數(shù)
* @return CommonResult<User>
*/
CommonResult<User> updateUser(@Valid User user);
}
UserService實(shí)現(xiàn)類:
package com.rtxtitanv.service.impl;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;
import com.rtxtitanv.service.UserService;
import org.springframework.stereotype.Service;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.service.impl.UserServiceImpl
* @description UserService實(shí)現(xiàn)類
* @date 2021/8/15 16:46
*/
@Service
public class UserServiceImpl implements UserService {
@Override
public CommonResult<User> updateUser(User user) {
return CommonResult.ok("更新用戶成功", user);
}
}
UserController中新增以下代碼:
@Resource
private UserService userService;
@PutMapping("/update")
public CommonResult<User> updateUser(@RequestBody User user) {
return userService.updateUser(user);
}
發(fā)送如下PUT請求,請求地址為http://localhost:8080/user/update:

六、編程式校驗(yàn)
通過Validator實(shí)例可以手動進(jìn)行參數(shù)校驗(yàn)。UserController中新增以下代碼:
@Resource
private Validator validator;
@PostMapping("/insert")
public CommonResult<User> insertUser(@RequestBody User user) {
if (!validator.validate(user).isEmpty()) {
throw new ConstraintViolationException(validator.validate(user));
}
return CommonResult.ok("添加用戶成功", user);
}
發(fā)送如下POST請求,請求地址為http://localhost:8080/user/insert:

七、自定義校驗(yàn)注解
如果自帶的校驗(yàn)注解無法滿足需求,還可以自定義校驗(yàn)注解。首先創(chuàng)建如下注解用于密碼校驗(yàn):
package com.rtxtitanv.annotation;
import com.rtxtitanv.validator.PasswordValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.annotation.Password
* @description 自定義密碼校驗(yàn)注解
* @date 2021/8/16 14:54
*/
@Documented
@Constraint(validatedBy = PasswordValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
String message() default "無效密碼";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
然后創(chuàng)建ConstraintValidator接口的實(shí)現(xiàn)類并重寫isValid方法:
package com.rtxtitanv.validator;
import com.rtxtitanv.annotation.Password;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.validator.PasswordValidator
* @description PasswordValidator
* @date 2021/8/16 15:01
*/
public class PasswordValidator implements ConstraintValidator<Password, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
// 密碼必須以大寫英文字母開頭,只包含英文字母、數(shù)字、下劃線,長度在6到20之間
return value.matches("^[A-Z][A-Za-z0-9_]{5,19}$");
}
}
創(chuàng)建如下注解用于密碼校驗(yàn):
package com.rtxtitanv.annotation;
import com.rtxtitanv.validator.RankValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.annotation.Rank
* @description 自定義用戶段位校驗(yàn)注解
* @date 2021/8/16 15:12
*/
@Documented
@Constraint(validatedBy = RankValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Rank {
String message() default "rank值無效";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
創(chuàng)建ConstraintValidator接口的實(shí)現(xiàn)類并重寫isValid方法,:
package com.rtxtitanv.validator;
import com.rtxtitanv.annotation.Rank;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.validator.RankValidator
* @description RankValidator
* @date 2021/8/16 15:18
*/
public class RankValidator implements ConstraintValidator<Rank, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
HashSet<String> ranks = new HashSet<>();
ranks.add("無段位");
ranks.add("青銅");
ranks.add("白銀");
ranks.add("黃金");
ranks.add("鉑金");
ranks.add("鉆石");
// 段位必須為無段位、青銅、白銀、黃金、鉑金、鉆石之一
return ranks.contains(value);
}
}
修改User類,使用自定義校驗(yàn)注解:
package com.rtxtitanv.model;
import com.rtxtitanv.annotation.Password;
import com.rtxtitanv.annotation.Rank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.*;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.model.User
* @description 用戶實(shí)體類
* @date 2021/8/15 16:28
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
@NotNull(message = "id不能為空")
private Long id;
@Length(min = 6, max = 20, message = "用戶名長度不小于6,不超過20")
@NotNull(message = "用戶名不能為空")
private String username;
@Password(message = "密碼必須以大寫英文字母開頭,只包含英文字母、數(shù)字、下劃線,長度在6到20之間")
@NotNull(message = "密碼不能為空")
private String password;
@Max(value = 60, message = "年齡最大為60")
@Min(value = 18, message = "年齡最小為18")
@NotNull(message = "年齡不能為空")
private Integer age;
@Email(message = "郵箱格式不正確")
@NotEmpty(message = "郵箱不能為空")
private String email;
@Rank(message = "段位必須為無段位、青銅、白銀、黃金、鉑金、鉆石之一")
private String rank;
}
發(fā)送如下POST請求,請求地址為http://localhost:8080/user/save:

八、分組校驗(yàn)
在參數(shù)校驗(yàn)時(shí)如果想針對不同的方法使用不同的校驗(yàn)規(guī)則,則可以使用分組校驗(yàn)。下面創(chuàng)建兩個用于分組校驗(yàn)的接口:
public interface AddUserGroup {}
public interface ModifyUserGroup {}
在User類的實(shí)例域id上添加以下注解:
@NotNull(message = "id不能為空", groups = ModifyUserGroup.class) @Null(message = "id必須為空", groups = AddUserGroup.class)
不寫
groups屬性,則為默認(rèn)分組。
UserController中新增以下方法:
@PostMapping("/add")
public CommonResult<User> addUser(@RequestBody @Validated(value = AddUserGroup.class) User user) {
return CommonResult.ok("新增用戶成功", user);
}
@PutMapping("/modify")
public CommonResult<User> modifyUser(@RequestBody @Validated(value = ModifyUserGroup.class) User user) {
return CommonResult.ok("修改用戶成功", user);
}
發(fā)送如下POST請求,請求地址為http://localhost:8080/user/add:

發(fā)送如下PUT請求,請求地址為http://localhost:8080/user/modify:

根據(jù)測試結(jié)果可知,方法addUser的參數(shù)使用的@Null校驗(yàn),方法modifyUser的參數(shù)使用的@NotNull校驗(yàn),成功實(shí)現(xiàn)了分組校驗(yàn)。
九、嵌套的參數(shù)校驗(yàn)
一個實(shí)體中嵌套一個實(shí)體時(shí),通過在嵌套的實(shí)體類型屬性上添加@Valid注解,可以對嵌套的參數(shù)進(jìn)行校驗(yàn)。
Account類:
package com.rtxtitanv.model;
import com.rtxtitanv.annotation.Password;
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.model.Account
* @description 賬戶實(shí)體類
* @date 2021/8/17 16:25
*/
@Data
public class Account {
@NotNull(message = "賬戶id不能為空")
private Long accountId;
@Size(min = 6, max = 20, message = "賬戶名長度不小于6,不超過20")
@NotNull(message = "賬戶名不能為空")
private String accountName;
@Password(message = "密碼必須以大寫英文字母開頭,只包含英文字母、數(shù)字、下劃線,長度在6到20之間")
@NotNull(message = "賬戶密碼不能為空")
private String accountPassword;
}
Order類:
package com.rtxtitanv.model;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.model.Order
* @description 訂單實(shí)體類
* @date 2021/8/17 16:55
*/
@Data
public class Order {
@NotNull(message = "訂單id不能為空")
private Long orderId;
@NotEmpty(message = "訂單號不能為空")
private String orderNumber;
@NotEmpty(message = "訂單描述信息不能為空")
private String orderDescription;
@Valid
private Account account;
}
OrderController:
package com.rtxtitanv.controller;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.Order;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.controller.OrderController
* @description OrderController
* @date 2021/8/17 17:00
*/
@RequestMapping("/order")
@RestController
@Validated
public class OrderController {
@PostMapping("/save")
public CommonResult<Order> saveOrder(@RequestBody @Valid Order order) {
return CommonResult.ok("訂單保存成功", order);
}
}
發(fā)送如下POST請求,請求地址為http://localhost:8080/order/save,發(fā)現(xiàn)嵌套的參數(shù)也被校驗(yàn):

代碼示例
Github:https://github.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-validatorGitee:https://gitee.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-validator
到此這篇關(guān)于SpringBoot2.x 參數(shù)校驗(yàn)的文章就介紹到這了,更多相關(guān)SpringBoot參數(shù)校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?Cookie與Session實(shí)現(xiàn)會話跟蹤詳解
session的工作原理和cookie非常類似,在cookie中存放一個sessionID,真實(shí)的數(shù)據(jù)存放在服務(wù)器端,客戶端每次發(fā)送請求的時(shí)候帶上sessionID,服務(wù)端根據(jù)sessionID進(jìn)行數(shù)據(jù)的響應(yīng)2022-11-11
java理論基礎(chǔ)函數(shù)式接口特點(diǎn)示例解析
這篇文章主要為大家介紹了java理論基礎(chǔ)函數(shù)式接口特點(diǎn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
Java日期時(shí)間字符串和毫秒相互轉(zhuǎn)換的方法
這篇文章主要為大家詳細(xì)介紹了Java日期時(shí)間字符串和毫秒相互轉(zhuǎn)換的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
HelloSpringMVC注解版實(shí)現(xiàn)步驟解析
這篇文章主要介紹了HelloSpringMVC注解版實(shí)現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java爬蟲實(shí)現(xiàn)Jsoup利用dom方法遍歷Document對象
本文主要介紹了Java爬蟲實(shí)現(xiàn)Jsoup利用dom方法遍歷Document對象,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
四個實(shí)例超詳細(xì)講解Java?貪心和枚舉的特點(diǎn)與使用
貪心算法是指,在對問題求解時(shí),總是做出在當(dāng)前看來是最好的選擇。也就是說,不從整體最優(yōu)上加以考慮,他所做出的是在某種意義上的局部最優(yōu)解,枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法2022-04-04

