wordpress會員插件上海搜索引擎優(yōu)化1
屬性校驗
- 前言
- @Validated基礎(chǔ)用法
- 集合校驗
- 分組校驗
- 嵌套校驗
- 自定義校驗器
源碼地址
前言
在項目開發(fā)過程中,經(jīng)常遇到需要對傳遞的參數(shù)進行校驗,比如某個參數(shù)字段是否為空、值的取值是否在約定范圍、格式是否合法等等,最原始的寫法,通過if判斷
@PostMappingpublic Result<Long> save(@RequestBody SystemUserDto systemUserDto) {if(systemUserDto.getUserName!=null){throw new BusinessException("用戶名稱不能為空");}if(systemUserDto.getMobile!=null){throw new BusinessException("用戶電話不能為空");}if(systemUserDto.getMobile().length()>11){throw new BusinessException("用戶電話長度不能超過11位");}return Result.ok(id);}
還有通過Spring框架提供的Assert
類,它能夠幫助我們確保方法參數(shù)符合預(yù)期,如果不符合會拋出IllegalArgumentException
,然后通過全局異常捕獲將錯誤按照統(tǒng)一格式返給前端。
示例:校驗字符串非空
import org.springframework.util.Assert;public void checkString(String input) {// 校驗字符串非空,如果為空則拋出異常Assert.hasText(input, "輸入字符串不能為空");
}
常用的一些方法
Assert.notNull(Object object,"object is required"); // 對象非空
Assert.isTrue(Object object,"object must be true"); // 對象必須為true
Assert.notEmpty(Collection collection,"collection must not be empty"); // 集合不能為空
Assert.hasLength(String text,"text must be specified"); // 字符不為null且字符長度不為0
Assert.hasText(String text,"text must not be empty"); // text不為null且必須至少包含一個非空的字符
Assert.isInstanceOf(Class class, Object object,"class must be of type[class]"); // object必須為class指定的類
最后一種就是使用@Valid
注解和 @Validated
注解,首先我們要了解這兩個注解的區(qū)別,然后著重描述一下@Validated
用法
1.來源不同
@Validated
:是Spring框架特有的注解,屬于Spring的一部分,也是JSR 303的一個變種。它提供了一些 @Valid
所沒有的額外功能,比如分組驗證。@Valid
:Java EE提供的標(biāo)準(zhǔn)注解,它是JSR
303規(guī)范的一部分,主要用于Hibernate Validation等場景。
2.注解位置
@Validated
: 用在類、方法和方法參數(shù)上,但不能用于成員屬性。@Valid
:可以用在方法、構(gòu)造函數(shù)、方法參數(shù)和成員屬性上。
3.是否支持分組
@Validated
:支持分組驗證,可以更細(xì)致地控制驗證過程。此外,由于它是Spring專有的,因此可以更好地與Spring的其他功能(如Spring的依賴注入)集成。@Valid
:主要支持標(biāo)準(zhǔn)的Bean驗證功能,不支持分組驗證。
4.嵌套驗證
@Validated
:不支持嵌套驗證。@Valid
:支持嵌套驗證,可以嵌套驗證對象內(nèi)部的屬性。
@Validated基礎(chǔ)用法
① 在根目錄build.gradle
引入校驗的依賴包
springValidationVersion = '3.3.2'
//Validation參數(shù)校驗
implementation "org.springframework.boot:spring-boot-starter-validation:${springValidationVersion}"
② 在對象需要校驗屬性上添加注解,比如我要驗證賬號不能為空
@NotBlank(message = "用戶賬號不能為空")
private String username;
其它類型的注解
注解 | 驗證的數(shù)據(jù)類型 | 描述 |
---|---|---|
@NotNull | 任意類型 | 驗證屬性不能為null |
@NotBlank | 字符串 | 驗證字符串屬性不能為空且長度必須大于0 |
@Size(min,max ) | CharSequence Collection Map Array | 字符串:字符串長度必須在指定的范圍內(nèi) Collection:集合大小必須在指定的范圍內(nèi) Map:map的大小必須在指定的范圍內(nèi) Array:數(shù)組長度必須在指定的范圍內(nèi) |
@Min | 整型類型 | 驗證數(shù)字屬性的最小值 |
@Max | 整型類型 | 驗證數(shù)字屬性的最大值 |
@DecimalMin | 數(shù)字類型 | 驗證數(shù)字屬性的最小值(包括小數(shù)) |
@DecimalMax | 數(shù)字類型 | 驗證數(shù)字屬性的最大值(包括小數(shù)) |
@Digits(integer,fraction) | 數(shù)字類型 驗證數(shù)字屬性的整數(shù)位數(shù)和小數(shù)位數(shù) | |
字符串類型 | 驗證字符串屬性是否符合Email格式 | |
@Pattern | 字符串 | 驗證字符串屬性是否符合指定的正則表達(dá)式 |
@Positive | 數(shù)字類型 | 驗證數(shù)值為正數(shù) |
@PositiveOrZero | 數(shù)字類型 | 驗證數(shù)值為正數(shù)或0 |
@Negative | 數(shù)字類型 | 驗證數(shù)值為負(fù)數(shù) |
@NegativeOrZero | 數(shù)字類型 | 驗證數(shù)值為負(fù)數(shù)或0 |
@AssertTrue | 布爾類型 | 參數(shù)值必須為 true |
@AssertFalse | 布爾類型 | 參數(shù)值必須為 false |
@Past | 時間類型(Date) | 參數(shù)值為時間,且必須小于 當(dāng)前時間 |
@PastOrPresent | 時間類型(Date) | 參數(shù)值為時間,且必須小于或等于 當(dāng)前時間 |
@Future | 時間類型(Date) | 參數(shù)值為時間,且必須大于 當(dāng)前時間 |
@FutureOrPresent | 時間類型(Date) | 參數(shù)值為時間,且必須大于或等于 當(dāng)前日期 |
③ 在接口校驗的對象前面添加上 @Validated
注解
@PostMapping
public Result<Long> save(@RequestBody @Validated SystemUserDto systemUserDto) {Long id=systemUserService.createUser(systemUserDto);return Result.ok(id);
}@PutMapping
public Result<Boolean> update(@RequestBody @Validated SystemUserDto systemUserDto) {systemUserService.updateUser(systemUserDto);return Result.ok(true);
}
④通過Apifox測試,可以看出返回的值并不能看出問題,因此下一步結(jié)構(gòu)化一下異常顯示
后端報錯
Resolved [org.springframework.web.bind.MethodArgumentNotValidException:Validation failed for argument [0] in public com.tps.cloud.response.Result<java.lang.Long>com.tps.cloud.system.controller.SystemUserController.save(com.tps.cloud.system.dto.SystemUserDto): [Field error in object 'systemUserDto' on field 'username': rejected value []; codes [NotBlank.systemUserDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [systemUserDto.username,username]; arguments []; default message [username]]; default message [用戶賬號不能為空]] ]
⑤ 給前端返回結(jié)構(gòu)化錯誤提示,需要配置全局異常捕獲類GlobalExceptionHandler
中添加捕獲MethodArgumentNotValidException
的方法
/*** validation Exception* @param exception* @return Result
*/
@ExceptionHandler({ MethodArgumentNotValidException.class })
public Result handleBodyValidException(MethodArgumentNotValidException exception) {FieldError fieldError = exception.getBindingResult().getFieldError();return Result.failed(String.format("%s", fieldError.getDefaultMessage()));
}
通過org.springframework.boot.autoconfigure.AutoConfiguration.imports
完成自動配置注冊
⑥ 通過Apifox測試,可以看出返回的值已經(jīng)結(jié)構(gòu)化
集合校驗
分組校驗
分組驗證是為了在不同的驗證場景下能夠?qū)ο蟮膶傩赃M行靈活地驗證,從而提高驗證的精細(xì)度和適用性。一般我們在對同一個對象進行保存或修改時,會使用同一個類作為入?yún)?。那么在?chuàng)建時需要校驗?zāi)硞€字段,但是更新的時候不需要校驗,這個時候就需要用到分組校驗了。
對于定義分組有兩點要特別注意:
- 定義分組必須使用接口
- 要校驗字段上必須加上分組,分組只對指定分組生效,不加分組不校驗
① 創(chuàng)建分組
用于創(chuàng)建時指定分組:
package com.tps.cloud.group;public interface AddGroup {}
用于更新時指定分組:
package com.tps.cloud.group;public interface UpdateGroup {}
② 在實體類上添加注解,我們只添加了AddGroup.class
分組
/**
* 用戶賬號
*/
@NotBlank(message = "用戶賬號不能為空",groups = {AddGroup.class})
private String username;
/**
* 密碼
*/
@NotBlank(message = "密碼不能為空",groups = {AddGroup.class})
private String password;
③ 在新增接口上添加分組,更新不添加分組,通過Apifox測試
@PostMapping
public Result<Long> save(@RequestBody @Validated({AddGroup.class}) SystemUserDto systemUserDto) {Long id=systemUserService.createUser(systemUserDto);return Result.ok(id);
}@PutMapping
public Result<Boolean> update(@RequestBody @Validated SystemUserDto systemUserDto) {systemUserService.updateUser(systemUserDto);return Result.ok(true);
}
④在實體類以及對應(yīng)接口上添加UpdateGroup.class
分組,通過Apifox測試
/*** 用戶賬號*/@NotBlank(message = "用戶賬號不能為空",groups = {AddGroup.class,UpdateGroup.class})private String username;/*** 密碼*/@NotBlank(message = "密碼不能為空",groups = {AddGroup.class,UpdateGroup.class})private String password;
@PutMappingpublic Result<Boolean> update(@RequestBody @Validated({UpdateGroup.class}) SystemUserDto systemUserDto) {systemUserService.updateUser(systemUserDto);return Result.ok(true);}
嵌套校驗
嵌套校驗(Nested Validation) 指的是在驗證對象時,對對象內(nèi)部包含的其他對象進行遞歸驗證的過程。當(dāng)一個對象中包含另一個對象作為屬性,并且需要對這個被包含的對象也進行驗證時,就需要進行嵌套校驗。
嵌套屬性指的是在一個對象中包含另一個對象作為其屬性的情況。換句話說,當(dāng)一個對象的屬性本身又是一個對象,那么這些被包含的對象就可以稱為嵌套屬性。
我們繼續(xù)以保存用戶接口為例:
① 創(chuàng)建SystemDeptDto
類,并添加驗證注解,現(xiàn)在把SystemDeptDto
作為SystemUserDto
的嵌套屬性
package com.tps.cloud.system.dto;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.tps.cloud.entity.TenantEntity;
import com.tps.cloud.group.AddGroup;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;/*** 部門傳輸類*/
@Data
public class SystemDeptDto{/*** 部門id*/@TableIdprivate Long id;/*** 部門名稱*/@NotBlank(message = "部門名稱不能為空",groups = {AddGroup.class})private String name;/*** 父部門id*/private Long parentId;/*** 顯示順序*/private Integer sort;/*** 負(fù)責(zé)人id*/private Long leaderUserId;/*** 聯(lián)系電話*/private String phone;/*** 郵箱*/private String email;/*** 部門狀態(tài)(0正常 1停用)*/@Validprivate Integer status;
}
public class SystemUserDto {...省略前面屬性/*** 部門*/private SystemDeptDto systemDept;
}
@PostMapping
public Result<Long> save(@RequestBody @Validated SystemUserDto systemUserDto) {Long id=systemUserService.createUser(systemUserDto);return Result.ok(id);
}
② 測試
自定義校驗器
有時,內(nèi)置的校驗器無法滿足您的需求。例如,您可能需要驗證用戶名是否唯一,這需要訪問數(shù)據(jù)庫。在這種情況下,您可以定義自己的校驗器。
要定義自定義校驗器,請創(chuàng)建一個實現(xiàn) javax.validation.ConstraintValidator
接口的類。在下面的示例中,我們將創(chuàng)建一個用于驗證用戶名是否唯一的校驗器:
① 創(chuàng)建校驗器UniqueUsernameValidator
,ConstraintValidator
包含以下兩種方法:
- 初始化方法 initialize:這個方法在驗證器的生命周期中僅被調(diào)用一次。它傳遞了與驗證器關(guān)聯(lián)的注解實例,允許驗證器從注解實例中提取和存儲配置詳情。
- 驗證方法isValid: 這是實現(xiàn)驗證邏輯的地方。這個方法對于每個要驗證的值都會被調(diào)用,并返回一個布爾值,表示數(shù)據(jù)是否符合約束條件。
package com.tps.cloud.system.constraint;import com.tps.cloud.system.service.SystemUserService;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import lombok.AllArgsConstructor;@AllArgsConstructor
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {private final SystemUserService systemUserService;@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (value == null) {return true;}return systemUserService.findByUsername(value) == null;}
}
② 創(chuàng)建一個自定義注解UniqueUsername
,以便在代碼中使用
package com.tps.cloud.system.constraint;import jakarta.validation.Constraint;
import jakarta.validation.Payload;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {UniqueUsernameValidator.class})
public @interface UniqueUsername {String message() default "用戶名已存在";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
② 在用戶賬號屬性上添加改注解
public class SystemUserDto {.../*** 用戶賬號*/@NotBlank(message = "用戶賬號不能為空",groups = {AddGroup.class,UpdateGroup.class})@UniqueUsernameprivate String username;...
}
③ 測試接口