91名师指路-头部
91名师指路

spring boot集成jsr303

由于某些原因,现在不支持支付宝支付,如需要购买源码请加博主微信进行购买,微信号:13248254750

一:为什么要使用jsr303

我们看下如下的代码,我们判断字段非空,要写一堆的代码,代码很臃肿,有没有一个工具来帮助我们简化开发呢,答案是有的。

@PostMapping("/update/point")
public ResultMessage updateRegionInfo(@RequestBody UpdatePickPointVO uv){
ResultMessage result=null;
if(StringUtils.isNotBlank(uv.getPrePlanNo()) && StringUtils.isNotBlank(uv.getRouteNo()) && StringUtils.isNotBlank(uv.getPrecinctNo())
&& null!=uv.getStoreList() && 0!=uv.getTimes() && StringUtils.isNotBlank(uv.getVisitWeek()) && StringUtils.isNotBlank(uv.getVisitWeekDay())){
result=batchPickPointService.updateRegionInfo(uv);
} else if(StringUtils.isBlank(uv.getPrePlanNo())){
result=ResultMessage.fail(PlanRevisionMsg.NOTEMPTY_PREPLANNO);
} else if(StringUtils.isBlank(uv.getRouteNo())){
result=ResultMessage.fail(PlanRevisionMsg.NOTEMPTY_ROUTENO);
} else if(StringUtils.isBlank(uv.getPrecinctNo())){
result=ResultMessage.fail(PlanRevisionMsg.NOTEMPTY_PRECINCTNO);
} else if(null==uv.getStoreList()){
result=ResultMessage.fail(PlanRevisionMsg.NOTEMPTY_STORELIST);
} else if(0==uv.getTimes()){
result=ResultMessage.fail(PlanRevisionMsg.NOTEMPTY_TIMES);
} else if(StringUtils.isBlank(uv.getVisitWeek())){
result=ResultMessage.fail(PlanRevisionMsg.NOTEMPTY_VISITWEEK);
} else if(StringUtils.isBlank(uv.getVisitWeekDay())){
result=ResultMessage.fail(PlanRevisionMsg.NOTEMPTY_VISITWEEKDAY);
} else{
result=ResultMessage.fail(PlanRevisionMsg.NETWORK_ANOMALY);
}
return result;
}


二:spring boot 集成jsr 303

2.1 引入pom

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>


2.2 在controller的方法上加上@Valid注意这是post请求

@PostMapping("/update/point")
public ResultMessage updateRegionInfo(@Valid @RequestBody UpdatePickPointVO uv){
ResultMessage result=batchPickPointService.updateRegionInfo(uv);
return result;
}


2.3 在vo类上加上必填注解。

public class UpdatePickPointVO {

@ApiModelProperty(value = "预计划编号")
@NotBlank(message = PlanRevisionMsg.NOTEMPTY_PREPLANNO)
private String prePlanNo;

@ApiModelProperty(value = "片区编号")
@NotBlank(message = PlanRevisionMsg.NOTEMPTY_PRECINCTNO)
private String precinctNo;


}


三:常用注解和说明。

 注解描述 
@Null验证对象是否为null
@NotNull验证对象是否不为 null,无法检验长度为0的字符串
@AssertTrue验证Boolean对象是否为true
@AssertFalse验证Boolean对象是否为false
@Min(value)值必须小于value,支持BigDecimal、BigInteger,byte、shot、int、long及其包装类
@Max(value)值必须大于value,支持BigDecimal、BigInteger,byte、shot、int、long及其包装类
@DecimalMin(value)值必须小于value,支持BigDecimal、BigInteger、CharSequence,byte、shot、int、long及其包装类
@DecimalMax(value)  值必须大于value,支持BigDecimal、BigInteger、CharSequence,byte、shot、int、long及其包装类
@Size(max=, min=)验证对象(CharSequence、Collection、Map、Array)长度是否在给定的范围之内
@Digits(integer=, fraction=) 必须是一个数字,integer表示该数字最多多少位,如:10,fraction表示小数位上限,如:2,表示最多10位,并最多2位小数
@Negative必须是一个负数
@NegativeOrZero必须是一个负数或0
@Positive必须是一个正数
@PositiveOrZero(message = )必须是个正数或0
@Past验证Date和 Calendar对象是否在当前时间之前
@PastOrPresent必须是一个过去的或当前的日期
@Future验证Date和Calendar对象是否在当前时间之后
@FutureOrPresent必须是一个未来的或当前的日期
@Pattern(regexp) 必须符合指定的正则表达式
@NotBlank(message = )检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格
@Email必须是电子邮箱地址
@NotEmpty检查约束元素是否为NULL或者是EMPTY
 @Length(min = , max = , message = )设置字段的最小长度和最大长度

四:注意事项。

4.1 验证int类型使用 @NotNull ,并使用Integer,不要使用int,因为使用int默认值为0,如下所示。

@NotNull(message = "xxx不能为空")
private Integer times;


4.2 验证String类型,使用 @NotBlank

@NotBlank(message = "xxx不能为空")
private String precinctNo;


4.3 验证List<String>中是否为空,使用 @NotEmpty

@NotEmpty(message = "xxList不能为空")
private List<String> storeList;


4.4 验证嵌套List<SalesInfoVO> 里面SalesInfoVO的字段是否为空。使用 @Valid

@Data
public class UpdateSalesInfo {

@ApiModelProperty(value = "xxlist")
@Valid
@NotEmpty(message = "xxList不能为空")
private List<SalesInfoVO> salesinfoList;

}


SalesInfoVO如下。

@Data
public class SalesInfoVO {

@ApiModelProperty(value = "xx编号")
@NotBlank(message = PlanRevisionMsg.NOTEMPTY_PRECINCTNO)
private String precinctNo;

@ApiModelProperty(value = "xxcode")
@NotBlank(message = PlanRevisionMsg.NOTEMPTY_EMPNO)
private String empNo;

@ApiModelProperty(value = "xx名称")
@NotBlank(message = PlanRevisionMsg.NOTEMPTY_EMPNAME)
private String empName;

}


4.5 使用正则验证,isExport字段不能为空,且只能输入Y或N

@ApiModelProperty(value = "是否导出: N为不导出; Y为导出")
@Pattern(regexp="^[Y|N]$", message = "只能输入N或Y")
@NotBlank(message = RodeoConstants.NOTEMPTY_ISEXPORT)
private String isExport;


4.6 设置字段长度限制

@Length(min = 0, max = 30, message = "最大长度为30")
private String bookmarkName;


4.7 设置只能输入指定的参数

@Pattern(regexp="(GET|POST|DELETE|PUT)", message = "请求方式只能输入GET、POST、DELETE、PUT")
@NotBlank(message="请求方式不能为空")
private String requestMethod;


4.8 设置只能输入年月日时分秒这种日期格式

@Pattern(regexp="([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))([ ])([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])", message = "调用时间格式只能为年月日时分秒")
@NotBlank(message="调用时间不能为空")
private String startDate;



五:post请求和get请求。

5.1 post请求:如果我们请求是post的话,按照本节2.2, 2.3 的步骤去做即可。

5.2 get请求:有两种方式。

5.3 方式一:将传参一个个的列在方法上,并加上验证规则之类的,还需要Controller上加上@Validated

注意:@Validated必须加在类上,加在方法上会不生效。

@Validated
@RestController
@RequestMapping("/product/brand")
public class BrandController {

@GetMapping("/select/info")
public ResultMessage selectStoreInfo(@NotBlank(message = PlanRevisionMsg.NOTEMPTY_PREPLANNO) String prePlanNo,
@NotBlank(message = PlanRevisionMsg.NOTEMPTY_STOREID) String storeId
) {
SelectStoreInfoVO sv = new SelectStoreInfoVO();
sv.setPrePlanNo(prePlanNo);
sv.setStoreId(storeId);
ResultMessage result = storeDetailsService.selectStoreInfo(sv);
return result;
}

}


5.4 方法二:将参数封装成vo,这里有大坑,我请教了大神才知道需要自己在统一异常里面自己去处理,文章末尾附上请求大神的帖子。

注意:@Validated必须加在方法上,加在类上会不生效。
@GetMapping("/select/info")
public ResultMessage selectStoreInfo(@Validated SelectStoreInfoVO sv){
ResultMessage result=storeDetailsService.selectStoreInfo(sv);
return result;
}

如果直接这样写的话,调用接口会返回如下图所示:

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors\nField error in object 'selectStoreInfoVO' on field 'storeId': rejected value [null]; codes [NotBlank.selectStoreInfoVO.storeId,NotBlank.storeId,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [selectStoreInfoVO.storeId,storeId]; arguments []; default message [storeId]]; default message [预计划编号不能为空!]\nField error in object 'selectSto

这样返回给前端肯定不友好。我们需要在统一异常处理里面拦截 BindException,然后给用户一个清晰的提示。


5.5 配置jsr config注解。(我在spring boot 项目中需要配置,但在spring cloud alibab项目中,不配置貌似也没问题,get方法也能正常使用)

package com.mszl.web.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

/**
* 描述:jsr303
*/
@Configuration
public class ValidationConfig {

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}


}


5.6 统一异常处理里面:

package com.mszl.blog.exception;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Set;
import com.mszl.blog.utils.ReturnMsgUtils;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;

/**
* 全局统一异常处理
*/
@ControllerAdvice
@Slf4j
public class GlobalDefultExceptionHandler {

// 声明要捕获的异常
@ExceptionHandler(Exception.class)
@ResponseBody
public ReturnMsgUtils defultExcepitonHandler(Exception ex) {
ReturnMsgUtils result=null;
if(ex instanceof BusinessException) {
BusinessException businessException = (BusinessException)ex;
result=new ReturnMsgUtils(businessException.getCode(), businessException.getMessage());
return result;
} else if(ex instanceof BindException){ // 解决jsr 303 get请求,多个参数封装到vo的统一异常处理
BindingResult bindResult=((BindException) ex).getBindingResult();
List<ObjectError> errorList=bindResult.getAllErrors();
String message=errorList.get(0).getDefaultMessage(); // 取第一条错误信息进行展示
return ReturnMsgUtils.fail(message);
}
else{
Throwable tb = ex;
StringWriter stringWriter= new StringWriter();
PrintWriter writer= new PrintWriter(stringWriter);
tb.printStackTrace(writer);
StringBuffer buffer= stringWriter.getBuffer();
log.error(buffer.toString());

result=ReturnMsgUtils.fail(buffer.toString());
return result;
}
}

// jsr303处理@RequestParam校验不通过异常:解决get请求单个参数,统一异常处理。
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ReturnMsgUtils validationError(ConstraintViolationException ex) {
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
String kk=violations.iterator().next().getMessage();
return ReturnMsgUtils.fail(kk);
}



}



参考资料:

https://blog.csdn.net/haibo_bear/article/details/90265741

https://www.cnblogs.com/mr-yang-localhost/p/7812038.html

https://blog.csdn.net/weixin_43137625/article/details/83181397


请教大神指导的贴:

https://blog.csdn.net/yuxiao97/article/details/98309120#comments_13655070



2020-10-29 15:22:18     阅读(973)

名师出品,必属精品    https://www.91mszl.com

联系博主    
用户登录遮罩层
x

账号登录

91名师指路-底部