前言
JSR303校验在Controller中使用@Valid 或@Validated进行参数校验。
@Valid 或@Validated区别
@Validated
Spring提供的
支持分组校验
可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
由于无法加在成员属性(字段)上,所以无法单独完成级联校验,需要配合@Valid
@Valid:
JDK提供的(标准JSR-303规范)
不支持分组校验
可以用在方法、构造函数、方法参数和成员属性(字段)上
可以加在成员属性(字段)上,能够独自完成级联校验
步骤
1.pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion> <!-- 排除自带的日志系统 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
</dependencies>
2.公共异常包装类
/**
* 公共Exception,用于对外统一输出异常信息
*/
public class AliException extends RuntimeException{
public AliException(String code, String message) {
super(code + " " + message);
}
}
3.公共响应类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 对外公共响应
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Response {
/* 成功/失败状态码 */
private String code;
/* 成功/失败信息 */
private String message;
/* 成功返回数据 */
private Object data;
}
4.全局异常处理配置
import cn.ali.resp.Response;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理
*/
@RestControllerAdvice(basePackages = "cn.ali.controller")
public class ExceptionAdvice {
/**
* 处理JSR303参数校验异常信息
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Response handleVaildException(MethodArgumentNotValidException e){
// 构建失败响应信息
Response response = new Response();
// 获取并组装JSR303参数校验失败异常信息
StringBuffer stringBuffer = new StringBuffer();
BindingResult bindingResult = e.getBindingResult();
bindingResult.getFieldErrors().forEach(item ->{
String message = item.getDefaultMessage();
String field = item.getField();
stringBuffer.append(field + ":" + message + " ");
});
response.setMessage(stringBuffer + "");
response.setCode("500");
response.setData(null);
return response;
}
/**
* 处理自定义异常对象拋出的异常信息
*/
@ExceptionHandler(value = AliException.class)
public Response handleIMsgException(AliException ex){
// 构建失败响应信息
Response response = new Response();
String[] eError = ex.getMessage().split(" ", 2);
response.setCode(eError[0]);
response.setMessage(eError[1]);
response.setData(null);
return response;
}
/**
* 处理不到的异常在此兜底处理
* 处理除去上面两种异常情况的其它异常信息(比如我们程序忽略或忘记处理的异常)
*/
@ExceptionHandler(value = Throwable.class)
public Response handleException(Throwable throwable){
// 构建失败响应信息
Response response = new Response();
response.setCode("500");
response.setMessage("服务器内部错误");
response.setData(null);
return response;
}
}
5.测试接口入参
/**
* 入参VO
*/
public class CommonVo {
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class UserVO{
@NotBlank(message = "id不能为空")
@Length(max = 32,message = "id最长不能超过32个字符")
private String id;
@NotBlank(message = "姓名不能为空")
@Length(min = 1,max = 10,message = "姓名字符长度为1-10个字符")
private String name;
@NotBlank
@Email(message = "邮箱格式不合法")
private String email;
@Min(value = 1,message = "年龄最小为1岁")
@Max(value = 100,message = "年龄最大为100岁")
private Integer age;
@Digits(integer = 5,fraction = 2,message = "薪资最多5位整数2位小数")
private BigDecimal salary;
@NotBlank
@Pattern(regexp = "[\"Y\",\"N\"]",message = "性别值必须为'Y'或'N'")
private String sex;
}
}
6.测试接口
import cn.ali.vo.CommonVo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/validateUser")
public void user(@Valid @RequestBody CommonVo.UserVO vo){
System.out.println("用户入参" + vo);
}
}
7.测试
8.其它补充
8.1.上面都是在Controller中校验那么如何在Service中校验
8.1.1.自定义校验方法
import cn.ali.exception.AliException;
import cn.hutool.core.bean.BeanUtil;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
/**
* 参数校验
*/
public class ParamsValidate {
/**
* 封装校验方法、方便在Service中进行参数校验
*/
public static <R> R validateJSR303(Object obj, Class<R> cls){
// 此处的data为前面验签后转JSONObject对象,非加密字符或JSON字符串
R r = BeanUtil.toBean(obj, cls);
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<Object>> set = validator.validate(r);
StringBuffer stringBuffer = new StringBuffer();
if(set != null && !set.isEmpty()){
for (ConstraintViolation<Object> constraintViolation : set) {
stringBuffer.append(constraintViolation.getPropertyPath() + ":" + constraintViolation.getMessage() + " ");
}
throw new AliException("400",stringBuffer.toString());
}
return r;
}
}
8.1.2.在UserController中新增测试接口validateUserByOwnMethod
/**
* 通过调用封装的validateJSR303方法,可在代码任意处进行参数校验;
*/
@PostMapping("/validateUserByOwnMethod")
public void validateUserByOwnMethod(@RequestBody CommonVo.UserVO vo){
CommonVo.UserVO userVO = ParamsValidate.validateJSR303(vo, vo.getClass());
System.out.println("用户入参" + userVO);
}