一、前言
JSR 是 Java Specification Requests 的縮寫,含義為 JAVA 規(guī)范提案。
JSR 303 - Bean Validation 規(guī)范, 正是一套基于 JavaBean 參數(shù)校驗的標(biāo)準(zhǔn)。
Hibernate Validator 是 JSR 303 的實現(xiàn),它提供了 JSR 303 規(guī)范中所有約束(constraint)的實現(xiàn),同時也對其作出一些拓展。
Spring Validation 是對 Hibernate validation 的二次封裝,用于支持 Spring MVC 參數(shù)校驗。
?? JSR 303 包含的注解:
驗證注解 |
驗證數(shù)據(jù)類型 |
說明 |
@Null |
任意類型 |
元素值為 Null |
@NotNull |
任意類型 |
元素值不為 Null |
@AssertTrue |
Bool |
元素為 true |
@AssertFalse |
Bool |
元素為 false |
@Min(value = 最小值) |
BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (數(shù)字)子類型 |
元素值需大于等于指定值 |
@Max(value = 最大值) |
BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (數(shù)字)子類型 |
元素值需小于等于指定值 |
@DecimalMin(value = 最小值) |
BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (數(shù)字)子類型 |
元素值需大于等于指定值 |
@DecimalMax(value = 最大值) |
BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (數(shù)字)子類型 |
元素值需小于等于指定值 |
@Size(min = 最小值, max = 最大值) |
String、Collection、Array 等 |
元素值的字符長度/集合大小需在指定的區(qū)間范圍內(nèi) |
@Digits(integer = 整數(shù)位數(shù), fraction = 小數(shù)位數(shù)) |
BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (數(shù)字)子類型 |
元素值整數(shù)位、小數(shù)位需小于對應(yīng)指定值 |
@Past |
DATE、Calendar、Time 等日期 |
元素值需在指定時間之前 |
@Future |
DATE、Calendar、Time 等日期 |
元素值需在指定時間之后 |
@Pattern(regexp = 正則式, flag = 標(biāo)志的模式) |
String 等 |
元素值與指定的正則式匹配 |
??hibernate.validator 擴展的注解:
驗證注解 |
驗證數(shù)據(jù)類型 |
說明 |
@NotBlank |
String 等 CharSequence 子類型 |
元素值不為空串(字符串不為 Null 且去除首尾空格后長度不為 0) |
@Email(regexp = 正則式, flag = 標(biāo)志的模式) |
String 等 |
元素值為電子郵箱格式,可通過 regexp、flag 屬性指定格式 |
@Length(min = 最小值, max = 最大值) |
String 等 |
元素值長度在指定區(qū)間范圍內(nèi) |
@NotEmpty |
String、Collection、Array 等 |
元素值不為 Null 且長度不為 0 |
@Range(min = 最小值, max = 最大值) |
BigDecimal、BigInteger、byte、short、int、long 等 Number 或 CharSequence (數(shù)字)子類型 |
元素值在指定區(qū)間范圍內(nèi) |
@URL |
String 等 |
元素值必須時合法的URL |
二、Spring Validation的使用
1、在項目pom.xml中引入依賴
<!-- JSR 303 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!-- 在 SpringBoot 項目中,若 SpringBoot 版本小于 2.3.x ,則此依賴已包含在 spring-boot-starter-web 中,無需添加額外依賴;
若 SpringBoot 版本大于 2.3.x ,則需手動引入依賴。-->
<!-- Hibernate Validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
2、 參數(shù)注解校驗的使用場景
場景一:直接入?yún)⒌男r?/h5>
①在類上添加 @Validated 注解,否則參數(shù)校驗無法生效;
②入?yún)⒅惺褂米⒔?@NotEmpty 等;
@Validated
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/saveUserName")
public String saveUserInfo(@NotEmpty String userName) {
System.out.println("userName:"+ userName +",保存成功");
return "success";
}
}
測試:
【請求URL】:
http://192.168.1.7:27100/user/saveUserName?userName=
【執(zhí)行結(jié)果】:
嚴(yán)重: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: saveUserInfo.userName: 不能為空] with root cause
javax.validation.ConstraintViolationException: saveUserInfo.userName: 不能為空
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
……
場景二:對象模型屬性的校驗
1、創(chuàng)建 User對象
@Data
public class User {
private Integer id;
@NotEmpty(message = "用戶名不能為空")
private String userName;
private Integer age;
@NotNull(message = "用戶密碼不能為空")
@Size(min = 5, max = 10,message = "密碼長度必須是5-10個字符")
private String password;
}
2、全局異常捕獲
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 方法直接入?yún)⑿r? *
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
public BaseResult resolveConstraintViolationException(ConstraintViolationException ex) {
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
if (!CollectionUtils.isEmpty(constraintViolations)) {
String errorMessage = constraintViolations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(", "));
return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, errorMessage);
}
return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, ex.getMessage());
}
/**
* 對象模型屬性校驗
*
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
if (!CollectionUtils.isEmpty(allErrors)) {
String errorMessage = allErrors.stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(", "));
return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, errorMessage);
}
return BaseResult.fail(ResultEnum.ILLEGAL_PARAMETER, ex.getMessage());
}
}
3、通用返回結(jié)構(gòu)體
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BaseResult<T> {
private Integer code;
private String message;
private T data;
public static <T> BaseResult<T> success() {
return new BaseResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), new JSONObject());
}
public static <T> BaseResult<T> fail(ResultEnum resultEnum, T data) {
return fail(resultEnum.getCode(), resultEnum.getMsg(), data);
}
@Override
public String toString() {
return "BaseResult{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
4、錯誤提示碼枚舉類
/**
* 錯誤提示碼
*/
public enum ResultEnum {
/** 狀態(tài)碼:未知錯誤 **/
UNKNOWN_ERROR(-1, "未知錯誤"),
/** 狀態(tài)碼:成功 **/
SUCCESS(0, "成功"),
/** 狀態(tài)碼:失敗 **/
FAIL(1,"失敗"),
/** 狀態(tài)碼:非法參數(shù) **/
ILLEGAL_PARAMETER(2,"非法參數(shù)");
private Integer code;
private String msg;
ResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
5、暴露的接口
① 在類上添加 @Validated 注解,否則參數(shù)校驗無法生效;
② 在入?yún)嶓w前添加 @Validated 注解;
@Validated
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/saveUserInfo")
public String saveUserInfo(@RequestBody @Validated User user) {
System.out.println("userName:"+ user.getUserName() +",保存成功");
return "success";
}
}
6、測試:
【請求URL】:http://192.168.1.7:27100/user/saveUserInfo
【請求方式】:POST
【請求參數(shù)】:
{
"userName":"張三"
}
【執(zhí)行結(jié)果】:
{
"code": 2,
"message": "非法參數(shù)",
"data": "用戶密碼不能為空"
}
場景三:參數(shù)注解校驗的分組驗證
1、新建分組接口
/**
* 分組接口:新增
*/
public interface Add {
}
/**
* 分組接口:更新
*/
public interface Update {
}
2、在校驗注解中使用 groups 屬性標(biāo)明分組
@Data
public class User {
@NotNull(message = "更新數(shù)據(jù)時,主鍵id不能為空", groups = Update.class)
private Integer id;
@NotEmpty(message = "用戶名不能為空")
private String userName;
private Integer age;
@NotNull(message = "用戶密碼不能為空")
@Size(min = 5, max = 10,message = "密碼長度必須是5-10個字符")
private String password;
}
【注】需要注意的是實體類中所有字段注解校驗規(guī)則默認屬于 Default 分組,接口入?yún)?@Validated 注解默認檢驗 Default 分組的校驗規(guī)則。當(dāng)接口入?yún)⑹褂?@Validated 顯式聲明非默認分組時,實體類中所有未顯式聲明分組的注解校驗將不會生效。
3、處理器方法
@Validated
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/updateUserInfo")
public String updateUserInfo(@RequestBody @Validated({Update.class}) User user) {
System.out.println("id:" + user.getId() + ",userName:"+ user.getUserName() +",更新成功");
return "success";
}
}
4、測試
【請求URL】:http://192.168.1.7:27100/user/updateUserInfo
【請求方式】:POST
【請求參數(shù)】:
{
"id":1,
"userName":"李四"
}
【執(zhí)行結(jié)果】:
id:1,userName:李四,更新成功
【注】入?yún)⒅袥]有傳“密碼”,也沒有校驗,若默認屬于 Default 分組的字段也需要校驗,可以寫成:
public String updateUserInfo(@RequestBody @Validated({Update.class, Default.class}) User user) {
場景四:級聯(lián)校驗
1、在實體類模型中,可能會存在集合類型或?qū)嶓w類型的成員變量,該成員中的字段仍然需要校驗。
在要校驗的對象類型的屬性/ list上使用 @Valid 注解:
@Data
public class User {
@NotNull(message = "更新數(shù)據(jù)時,主鍵id不能為空", groups = Update.class)
private Integer id;
@NotEmpty(message = "用戶名不能為空")
private String userName;
private Integer age;
@NotNull(message = "用戶密碼不能為空")
@Size(min = 5, max = 10,message = "密碼長度必須是5-10個字符")
private String password;
@Valid
private List<Car> cars;
}
@Data
public class Car {
@NotNull(message = "車牌號碼不能為空")
private String plateCode;
@NotNull(message = "車牌顏色不能為空")
private String plateColor;
}
2、處理器方法
@Validated
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/saveUserInfo")
public String saveUserInfo(@RequestBody @Validated User user) {
System.out.println("userName:"+ user.getUserName() +",保存成功");
return "success";
}
}
3、測試
【請求URL】:http://192.168.1.7:27100/user/saveUserInfo
【請求方式】:POST
【請求參數(shù)】:
{
"userName": "李四",
"password": "123456",
"cars": [
{
"plateCode": "京A0001",
"plateColor": "1"
},
{
"plateCode": "京A0002"
}
]
}
【執(zhí)行結(jié)果】:
{
"code": 2,
"message": "非法參數(shù)",
"data": "車牌顏色不能為空"
}
場景五:自定義注釋校驗
如下:校驗用戶下車牌號碼必須包含“京A”,否則返回提示;
1、自定義注釋
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {MustContainKeyValidator.class})
public @interface MustContainKey {
//默認錯誤信息
String message() default "必須含有指定關(guān)鍵字";
//分組
Class<?>[] groups() default {};
// 負載
Class<? extends Payload>[] payload() default {};
}
2、真正的校驗者類,實現(xiàn) ConstraintValidator 接口
public class MustContainKeyValidator implements ConstraintValidator<MustContainKey,String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!StringUtils.isEmpty(value) && !value.contains("京A")) {
// 獲取默認提示消息
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
System.out.println(defaultConstraintMessageTemplate);
// 禁用默認提示信息
// context.disableDefaultConstraintViolation();
//設(shè)置提示語
// context.buildConstraintViolationWithTemplate("must contain key").addConstraintViolation();
return false;
}
return true;
}
}
3、在實體類的屬性上添加該注釋
@Data
public class User {
@NotNull(message = "更新數(shù)據(jù)時,主鍵id不能為空", groups = Update.class)
private Integer id;
@NotEmpty(message = "用戶名不能為空")
private String userName;
private Integer age;
@NotNull(message = "用戶密碼不能為空")
@Size(min = 5, max = 10,message = "密碼長度必須是5-10個字符")
private String password;
@Valid
private List<Car> cars;
}
@Data
public class Car {
@MustContainKey
@NotNull(message = "車牌號碼不能為空")
private String plateCode;
@NotNull(message = "車牌顏色不能為空")
private String plateColor;
}
4、測試文章來源:http://www.zghlxwxcb.cn/news/detail-472158.html
【請求URL】:http://192.168.1.7:27100/user/saveUserInfo
【請求方式】:POST
【請求參數(shù)】:
{
"userName": "李四",
"password": "123456",
"cars": [
{
"plateCode": "京A0001",
"plateColor": "1"
},
{
"plateCode": "浙C0002",
"plateColor": "2"
}
]
}
【執(zhí)行結(jié)果】:
{
"code": 2,
"message": "非法參數(shù)",
"data": "必須含有指定關(guān)鍵字"
}
以上就是所有關(guān)于 Spring Validation 的介紹,感興趣的同學(xué)歡迎點贊+收藏!文章來源地址http://www.zghlxwxcb.cn/news/detail-472158.html

到了這里,關(guān)于Spring Validation 接口入?yún)⑿r灥奈恼戮徒榻B完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!