前言:
在上一篇文章中介绍了参数验证的简单使用,本文将介绍级联校验,分组校验,以及Springboot支持的在Service进行参数校验
一.级联校验
1.实体类
@Data
@JsonInclude(value= JsonInclude.Include.NON_NULL)
public class MyUser {
@NotEmpty
@NotNull
private String id;
@NotNull
@NotEmpty
private String name;
@NotNull
@NotEmpty
@Email
private String email;
private String message;
@Valid
@NotNull
private Department department;
}
@Data
@JsonInclude(value= JsonInclude.Include.NON_NULL)
public class Department {
@NotEmpty
@NotNull
private String id;
@NotEmpty
@NotNull
private String name;
}
这里新建了两个实体类,其中Department类对象作为MyUser类的成员变量,在上一篇文章我们知道了数据校验功能注解的基本使用,而在此稍微有些不同,当我们校验MyUser对象时需要对该对象的成员变量Deparment对象也进行数据校验(即级联校验),故需要在MyUser成员变量Department department上添加@Vail注解,即能开启对department对象的数据校验。
2.控制层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResultVo addUser(@RequestBody @Vail MyUser myuser ){
//...
}
}
3.异常拦截器
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultVo MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
//创建了一个map,key为变量名称,value为错误信息
Map<String,String> map=new HashMap<>();
List<FieldError> list=e.getBindingResult().getFieldErrors();
for (FieldError error : list) {
//参数的名称和错误信息
map.put(error.getField(),error.getDefaultMessage());
}
//封装了更加友好的数据返回给前端
return ResultVo.fail(ErrorCode.PARAM_ERROR,map);
}
4.测试
此处使用Postman进行测试
二.分组校验
1.实体类
@Data
@JsonInclude(value= JsonInclude.Include.NON_NULL)
public class MyUser {
public interface add{
}
public interface update{
}
@NotEmpty(groups = update.class)
@NotNull(groups = update.class)
@Null(groups = add.class)
private String id;
@NotNull
@NotEmpty
private String name;
@NotNull
@NotEmpty
@Email
private String email;
private String message;
@Valid
@NotNull
private Department department;
}
在实际项目中会遇到这么一种类型的情况:用户注册时,前端返回的用户的数据是不具有用户id的,所以此时需要保证用户id为空,而当用户修改自己的信息时,此时是具有用户id的,故需保证用户id不为空。对于id在不同时期需要有不同的验证策略,故使用分组验证。
分组验证需要在实体类中添加内部接口,每一个接口即是一种类别,之后再往需要进行分组验证的成员变量的注解属性值groups添加接口字节码文件即可,共同拥有一种接口字节码文件的注解就是一组验证策略。
2.控制层
@PostMapping
public ResultVo addUser(@RequestBody @Validated({MyUser.add.class, Default.class}) MyUser myUser){
//...
}
此处需要使用Spring提供的@Validated注解以支持分组校验。
注意:在@Validated添加分组时,需添加Defaut.class,表示其他无分组的验证注解一并生效,若无添加就只是add.class组别的验证注解生效而已
3.异常拦截器
同上文一致,此处不再赘述
4.测试
三.Service层验证
项目中有时无法在控制层就完成数据校验工作,就需要在业务层完成,SpringBoot也对业务层的数据校验提供了支持
1.实体类
@Data
@JsonInclude(value= JsonInclude.Include.NON_NULL)
public class MyUser {
@NotEmpty
@NotNull
private String id;
@NotNull
@NotEmpty
private String name;
@NotNull
@NotEmpty
@Email
private String email;
private String message;
@Valid
@NotNull
private Department department;
}
2.控制层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResultVo addUser(@RequestBody String userMessage){
JSONObject jsonObject= JSON.parseObject(userMessage);
String code=(String)jsonObject.get("code");
//提取出验证码后可对其进行验证
//这里假装进行了验证(手动滑稽)
if (code!=null&&code.equals("12345")) {
MyUser myUser=userService.addUser(JSONObject.parseObject(userMessage,MyUser.class));
return ResultVo.success(myUser);
}
return ResultVo.fail(ErrorCode.CODE_ERROR);
}
此处前端的数据除了用户信息外还有验证码code,在控制层对验证码进行检验,若验证码符合,再将json中的数据封装进对象中传递给业务层
3.业务层
@Service
@Validated
public class UserService {
public MyUser addUser(@Vail MyUser myUser){
//...
return myUser;
}
在业务层需要对其开启SpringBoot对业务层参数校验的支持,即添加@Validated注解和在需要参数校验的变量前添加@Vail注解即可
4.异常拦截
此处抛出的异常与在控制层进行参数校验抛出的异常不是同一个,故需再写一个异常拦截器
@ControllerAdvice
@ResponseBody
@JsonInclude(value= JsonInclude.Include.NON_NULL)
public class GlobalExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
public ResultVo ConstraintViolationExceptionHandler(ConstraintViolationException e){
Map<String,String> map=new HashMap<>();
Set<ConstraintViolation<?>> set=e.getConstraintViolations();
for (ConstraintViolation cv : set){
String[] path=cv.getPropertyPath().toString().split("\\.");
String message=cv.getMessage();
map.put(path[path.length-1],message);
}
return ResultVo.fail(ErrorCode.PARAM_ERROR,map);
}
5.测试
三点一.Service的分组校验加级联验证
第三部分介绍了Service层的参数校验,是SpringBoot提供支持的,但是SpringBoot貌似并没有提供对于Service层的分组校验,楼主搜遍了各大博客网站没有找到一篇Service层分组校验的技术博客,这就非常的蛋疼了。
那咋办嘛,只能自己写个参数校验器咯
1.实体类
@Data
@JsonInclude(value= JsonInclude.Include.NON_NULL)
public class MyUser {
public interface add{
}
public interface update{
}
@NotEmpty(groups = update.class)
@NotNull(groups = update.class)
@Null(groups = add.class)
private String id;
@NotNull
@NotEmpty
private String name;
@NotNull
@NotEmpty
@Email
private String email;
private String message;
@Valid
@NotNull
private Department department;
}
@Data
@JsonInclude(value= JsonInclude.Include.NON_NULL)
public class Department {
@Null(groups = MyUser.add.class)
@NotEmpty(groups = MyUser.update.class)
@NotNull(groups = MyUser.update.class)
private String d_id;
@NotEmpty
@NotNull
private String d_name;
}
2.控制层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResultVo addUser(@RequestBody String userMessage){
JSONObject jsonObject= JSON.parseObject(userMessage);
String code=(String)jsonObject.get("code");
//提取出验证码后可对其进行验证
if (code!=null&&code.equals("12345")) {
MyUser myUser=userService.addUser(JSONObject.parseObject(userMessage,MyUser.class));
return ResultVo.success(myUser);
}
return ResultVo.fail(ErrorCode.CODE_ERROR);
}
3.参数校验器
@Component
public class ValidationUtil {
//SpringBoot能将Validator直接注入
@Autowired
private Validator validator;
public void validateTest(Object object,Class[] classes){
Set<ConstraintViolation<Object>> set = validator.validate(object, classes);
//set.size大于0则表明参数出错
if (set.size()>0){
Map<String,String> map=new HashMap<>();
for (ConstraintViolation<Object> cv : set){
String[] param=cv.getPropertyPath().toString().split("\\.");
String message=cv.getMessage();
map.put(param[param.length-1],message);
}
//抛出业务异常,并且需要根据业务异常再写一个异常拦截器,此处不再赘述
throw new MyException(map);
}
}
}
4.业务层
@Service
public class UserService {
//将参数校验工具注入
@Autowired
ValidationUtil validationUtil;
public MyUser addUser( MyUser myUser){
//调用参数校验
validationUtil.validateTest(myUser,new Class[]{Default.class,MyUser.add.class});
//...
return myUser;
}
5.测试
总结
如有错误,还劳烦大佬指正,感激不尽
2020.2.14
午夜 11:44