springboot项目中@Valid注解的使用-笔记
@Vaild介绍
@Valid 是jdk提供的,是jsr-303规范 ,@Valid支持复合bean的嵌套校验,可以用在方法、字段、构造器和参数上
@Valid嵌套校验测试
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import javax.validation.Valid;
import java.time.LocalDateTime;
import com.alibaba.fastjson.JSONPath;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
public class Test0529Api {
@PostMapping("/t123")
public UserReq test123(@Valid @RequestBody UserReq req){
log.info("req:{}",req.toString());
return req;
}
}
@Data
public class UserReq {
@NotBlank
@Size(max = 30,min = 2,message = "长度需要在2-30之间")
private String account;
@Email
private String email;
@Pattern(regexp = "(?:0|86|\\+86)?1[3-9]\\d{9}",message = "必须是手机号")
private String phone;
/**
* 必须是一个过去的或当前的日期,jdk提供的
*/
@PastOrPresent
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") //以特定格式字符串接收请求的日期参数,jackson会自动转为LocalDateTime
private LocalDateTime startDt;
/**
* 必须是一个将来的或当前的日期,jdk提供的
*/
@FutureOrPresent
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endDt;
/**
* 嵌套校验
*/
@Valid
private CompanyDTO company;
/**
* hibernate.validator包提供的 范围限制注解 @Range ,限制数值的大小
*/
@Range(min = 1,max = 99)
private Integer testCount;
}
@Data
public class CompanyDTO {
@NotNull
private Integer id;
@NotBlank
@Length(max = 30,min=4,message = "长度需要在4-30之间")
private String name;
@Pattern(regexp ="(010|02\\d|0[3-9]\\d{2})-?(\\d{6,8})",message = "必须是国内固定电话号码")
private String tel;
}
测试发现抛MethodArgumentNotValidException异常,经过exceptionHandler处理后,返回
@ExceptionHandler({MethodArgumentNotValidException.class})
public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
log.error("自动抛的MethodArgumentNotValidException",e);
// 从异常对象中拿到ObjectError对象
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
log.error("objectError:{}", JSONObject.toJSONString(objectError, true));
String field = String.valueOf(JSONPath.eval(objectError, "$.field"));
log.error("field:{}", field);
String objName = objectError.getObjectName();
System.err.println(objName);
// 然后提取错误提示信息进行返回
return new ResultVO<>(ResultCodeEnum.VALIDATE_FAILED,
objName + "." + field + objectError.getDefaultMessage());
}
返回json打印错误提示信息
{
"code": 9996,
"msg": "参数校验异常",
"content": "userReq.testCount需要在1和99之间",
"timestamp": 1685347134748,
"traceDateTime": "2023-05-29 15:58:54"
}
@Valid校验,人为干预BindingResult
人为干预BindingResult的,需要人工抛出异常
/**
* @Valid+BindingResult 人为干预Binding判断
*
* @param req
* @param bindingResult 当增加了BindingResult对象后,参数校验失败时不会自动抛异常,需要人工进行异常处理
* @return
*/
@PostMapping("/test003")
public UserReq test003(@Valid @RequestBody UserReq req, BindingResult bindingResult){
log.info("req:{}",req.toString());
//人为干预BindingResult ,人工判断参数是否合法/有错误
if (bindingResult.hasErrors()){
//人工抛出异常, 可以对每个参数做特殊的判断
ObjectError objectError = bindingResult.getAllErrors().get(0);
String field = String.valueOf(JSONPath.eval(objectError, "$.field"));
String objName = objectError.getObjectName();
//这里把bind的参数异常 手动抛出为非法参数异常
throw new IllegalArgumentException(objName + "." + field + objectError.getDefaultMessage());
}
return req;
}
{
"code": 9988,
"msg": "非法参数异常",
"content": "userReq.testCount需要在1和99之间",
"timestamp": 1685348120036,
"traceDateTime": "2023-05-29 16:15:20"
}
手动抛出的异常也要在exceptionHandler里处理
/**
* 手动抛出的或 Assert断言的 非法参数异常
*
* @param e IllegalArgumentException
* @return ResultVO
*/
@ExceptionHandler({IllegalArgumentException.class})
public ResultVO illegalArgumentExceptionHandler(IllegalArgumentException e) {
log.error("人工抛出异常的IllegalArgumentException",e);
return new ResultVO<>(ResultCodeEnum.VALIDATE_FAILED,
e.getMessage());
}
兜底方案,就是直接捕获处理Exception.class
/**
* 未知异常
*
* @param e
* @return
*/
@ExceptionHandler({Exception.class})
public ResultVO otherExceptionHandle(Exception e) {
log.error("otherExceptionHandle", e);
return new ResultVO(ResultCodeEnum.ERROR, e.getMessage());
}