如何使用Bean Validation 解決業(yè)務(wù)中參數(shù)校驗
前言
在開發(fā)中經(jīng)常需要寫一些字段校驗的代碼,比如字段非空,字段長度限制,郵箱格式驗證等等,寫這些與業(yè)務(wù)邏輯關(guān)系不大的代碼個人感覺有點麻煩:
驗證代碼繁瑣,重復(fù)勞動
方法內(nèi)代碼顯得冗長
每次要看哪些參數(shù)驗證是否完整,需要去翻閱驗證邏輯代碼
敘述
Bean Validation是一個通過配置注解來驗證參數(shù)的框架,它包含兩部分Bean Validation API和Hibernate Validator。
Bean Validation API是Java定義的一個驗證參數(shù)的規(guī)范。
Hibernate Validator是Bean Validation API的一個實現(xiàn)。
@Valid和Validated的比較
Spring Validation驗證框架對參數(shù)的驗證機制提供了@Validated(Spring's JSR-303規(guī)范,是標(biāo)準JSR-303的一個變種),javax提供了@Valid(標(biāo)準JSR-303規(guī)范),配合BindingResult可以直接提供參數(shù)驗證結(jié)果。
@Valid : 沒有分組功能,可以用在方法、構(gòu)造函數(shù)、方法參數(shù)和成員屬性(field)上,如果一個待驗證的pojo類,其中還包含了待驗證的對象,需要在待驗證對象上注解@valid,才能驗證待驗證對象中的成員屬性
@Validated :提供分組功能,可以在入?yún)Ⅱ炞C時,根據(jù)不同的分組采用不同的驗證機制,用在類型、方法和方法參數(shù)上。但不能用于成員屬性(field)。
兩者都可以用在方法入?yún)⑸?,但都無法單獨提供嵌套驗證功能,都能配合嵌套驗證注解@Valid進行嵌套驗證。
嵌套驗證示例:
public class ClassRoom{
@NotNull
String name;
@Valid // 嵌套校驗,校驗參數(shù)內(nèi)部的屬性
@NotNull
Student student;
}
@GetMapping("/room") // 此處可使用 @Valid 或 @Validated, 將會進行嵌套校驗
public String validator(@Validated ClassRoom classRoom, BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok";
}
BindingResult 的使用
BindingResult必須跟在被校驗參數(shù)之后,若被校驗參數(shù)之后沒有BindingResult對象,將會拋出BindException。
@GetMapping("/room")
public String validator(@Validated ClassRoom classRoom, BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok";
}
不要使用 BindingResult 接收String等簡單對象的錯誤信息。簡單對象校驗失敗,會拋出 ConstraintViolationException。主要就是接不著,你要寫也算是沒關(guān)系…
// ❌ 錯誤用法,也沒有特別的錯,只是 result 是接不到值。
@GetMapping("/room")
@Validated // 啟用校驗
public String validator(@NotNull String name, BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok";
}
修改校驗失敗的提示信息
可以通過各個校驗注解的message屬性設(shè)置更友好的提示信息。
public class ClassRoom{
@NotNull(message = "Classroom name must not be null")
String name;
@Valid
@NotNull
Student student;
}
@GetMapping("/room")
@Validated
public String validator(ClassRoom classRoom, BindingResult result, @NotNull(message = "姓名不能為空") String name) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok";
}
message屬性配置國際化的消息也可以的,message中填寫國際化消息的code,在拋出異常時根據(jù)code處理一下就好了。
@GetMapping("/room")
@Validated
public String validator(@NotNull(message = "demo.message.notnull") String name) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok";
}
// message_zh_CN.properties demo.message.notnull=xxx消息不能為空 // message_en_US.properties demo.message.notnull=xxx message must no be null
hibernate-validator 的使用
1.引入pom
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.1.Final</version>
</dependency>
2.dto入?yún)ο髮傩约尤胱⒔?/strong>
@Data
public class UserModel implements Serializable {
private String id;
@NotBlank(message = "用戶名不能為空")
private String name;
@NotNull(message = "性別不能不填")
private Byte gender;
@NotNull(message = "年齡不能不填")
@Min(value = 0,message = "年齡必須大于0歲")
@Max(value = 150,message = "年齡必須小于150歲")
private Integer age;
@NotBlank(message = "手機號不能不填")
private String telphone;
private String registerMode;
private String thirdPartyId;
private String encrptPassward;
}
方法一:3.controller方法入?yún)⒓尤胄r?@Validated )
@GetMapping("/getUser")
public String validator(@Validated UserModel userModel , BindingResult result) {
if (result.hasErrors()) {
return result.getFieldError().getDefaultMessage();
}
return "ok";
}
方法二:3.自定義封裝ValidatorImpl類
@Component
public class ValidatorImpl implements InitializingBean{
private Validator validator;
//實現(xiàn)校驗方法并返回校驗結(jié)果
public ValidationResult validate(Object bean){
final ValidationResult result=new ValidationResult();
Set<ConstraintViolation<Object>> validate = validator.validate(bean);
if (validate.size()>0) {
result.setHasError(true);
validate.forEach(constraintViolation->{
String errMsg=constraintViolation.getMessage();
String propertyName=constraintViolation.getPropertyPath().toString();
result.getErrorMsgMap().put(propertyName,errMsg);
});
}
return result;
}
@Override
public void afterPropertiesSet() throws Exception {
this.validator= Validation.buildDefaultValidatorFactory().getValidator();
}
}
方法二:4.自定義封裝ValidationResult 類
public class ValidationResult {
public boolean hasError=false;
private Map<String,String> errorMsgMap=new HashMap<>();
//實現(xiàn)通用的通過格式化字符串信息獲取錯誤結(jié)果的msg方法
public String getErrMsg(){
return StringUtils.join(errorMsgMap.values().toArray(),",");
}
public boolean isHasError() {
return hasError;
}
public void setHasError(boolean hasError) {
this.hasError = hasError;
}
public Map<String, String> getErrorMsgMap() {
return errorMsgMap;
}
public void setErrorMsgMap(Map<String, String> errorMsgMap) {
this.errorMsgMap = errorMsgMap;
}
}
5.controller方法入?yún)⒓尤胄r?/strong>
@Autowired
private ValidatorImpl validator;
@Override
@Transactional(rollbackFor = Exception.class)
public void register(UserModel userModel) throws BusinessException {
UserDo userDo=new UserDo();
if (userModel == null) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
}
//validate進行入?yún)⑿r?
ValidationResult validate = validator.validate(userModel);
if (validate.isHasError()){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,validate.getErrMsg());
}
}
Bean Validation 的約束
@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)被注釋的元素必須符合指定的正則表達式
Hibernate Validator 附加的約束
Hibernate Validator 附加的 constraint:
@Email被注釋的元素必須是電子郵箱地址@Length被注釋的字符串的大小必須在指定的范圍內(nèi)@NotEmpty被注釋的字符串的必須非空@Range被注釋的元素必須在合適的范圍內(nèi)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java源碼解析ArrayList及ConcurrentModificationException
今天小編就為大家分享一篇關(guān)于Java源碼解析ArrayList及ConcurrentModificationException,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01
Spring AOP實現(xiàn)功能權(quán)限校驗功能的示例代碼
本篇文章主要介紹了Spring AOP實現(xiàn)功能權(quán)限校驗功能的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
MyBatis在注解上使用動態(tài)SQL方式(@select使用if)
這篇文章主要介紹了MyBatis在注解上使用動態(tài)SQL方式(@select使用if),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
Java實現(xiàn)學(xué)生選課管理系統(tǒng)
這篇文章主要為大家詳細介紹了Java實現(xiàn)學(xué)生選課管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07

