JSR303的简单应用
JSR的介绍
JSR
JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
JSR303
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
简单校验的实际运用
第1步:属性添加注解
给javabean中的属性添加校验注解(校验注解可以在javax.validation.constraints中查看
/**
* 品牌id
*/
@NotNull(message = "修改信息必须指定品牌id",groups = {UpdateGroup.class})
@Null(message = "新增信息不能指定品牌id",groups = {AddGroup.class})
@TableId
private Long brandId;
message为校验不通过时,给出的提示信息;groups为分组校验(这个后面在讲
第2步:形参添加注解
在controller形参列表中对应的javabean前面添加**@Valid**注解
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid(AddGroup.class) @RequestBody BrandEntity brand ){
brandService.save(brand);
return R.ok();
}
此时已经开启了数据校验功能额
第3步:获取javabean校验失败的属性和原因
紧跟被@Valid注解的形参后,使用BindingResult result,可以通过result对象获取校验失败的属性和失败原因
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid(AddGroup.class) @RequestBody BrandEntity brand , BindingResult result){
Map<String,String> map = new HashMap<>();
//判断是否含有校验失败
if(result.hasErrors()){
//1.获取校验的错误信息
result.getFieldErrors().forEach((item)->{
//FieldError 获取错误提示信息
String defaultMessage = item.getDefaultMessage();
//获取错误的属性名称
String field = item.getField();
map.put(field,defaultMessage);
});
//将校验不通过的属性和失败原因放入R返回值对象中
return R.error(400,"提交的数据不合法").put("data",map);
}
else{
brandService.save(brand);
}
brandService.save(brand);
return R.ok();
}
此时就能获的校验不通过的属性和原因
优化:如果在controller中有很多方法都需要进行数据校验之后的处理,为了方便,我们可以把数据校验之后的处理放在全局异常处理类中
上面的步骤中,第一步和第二步不变,将第三步中的处理逻辑注释
第3.1步
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid(AddGroup.class) @RequestBody BrandEntity brand /*, BindingResult result*/){
// Map<String,String> map = new HashMap<>();
// //判断是否含有校验失败
// if(result.hasErrors()){
// //1.获取校验的错误信息
// result.getFieldErrors().forEach((item)->{
// //FieldError 获取错误提示信息
// String defaultMessage = item.getDefaultMessage();
// //获取错误的属性名称
// String field = item.getField();
// map.put(field,defaultMessage);
// });
// return R.error(400,"提交的数据不合法").put("data",map);
// }
// else{
// brandService.save(brand);
// }
/**
* 将数据校验出现的异常放到统一异常处理中处理
*/
brandService.save(brand);
return R.ok();
}
第3.2步 编写全局异常处理类
package com.watching.gulimall.product.exception;
import com.watching.common.exception.BizCodeEnume;
import com.watching.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* @author Watching
* * @date 2022/10/8
* * Describe:集中处理所有异常
*/
@Slf4j
@RestControllerAdvice(basePackages = "com.watching.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
/**
* 处理数据校验异常
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e){
log.error("数据校验出现异常:{};异常类型:{}",e.getMessage(),e.getClass());
//获取数据校验错误的信息
Map<String,String> map = new HashMap<>();
e.getBindingResult().getFieldErrors().forEach((item)->{
String field = item.getField();
String defaultMessage = item.getDefaultMessage();
map.put(field,defaultMessage);
});
return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",map);
}
}
在全局异常处理类中可以使用MethodArgumentNotValidException e的对象获取 BindingResult对象。然后使用第3步中相同的逻辑进行处理
分组校验的实际运用
有些情况,controller中不同的方法需要对不同的属性进行校验,此时就需要用到分组校验
第1步 创建标识性接口
/**
* @author Watching
* * @date 2022/10/8
* * Describe: jsr303分组校验标识
*/
public interface AddGroup {
}
/**
* @author Watching
* * @date 2022/10/8
* * Describe: jsr303分组校验标识
*/
public interface UpdateGroup {
}
第2步 在属性的校验注解中添加groups属性
/**
* 品牌id
*/
@NotNull(message = "修改信息必须指定品牌id",groups = {UpdateGroup.class})
@Null(message = "新增信息不能指定品牌id",groups = {AddGroup.class})
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名称不能空",groups = {UpdateGroup.class,AddGroup.class})
private String name;
表明这些属性都在不同的校验分组下
第3步 更改controller形参列表中的注解@Valid => @Validated
在controller形参列表中给对应形参前面加上 @Validated({AddGroup.class}) 表明该方法只需要校验在AddGroup分组下的属性。将 @Valid 改为 @Validated({“此处写标识性接口”})
@Validated代表分组校验
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand /*, BindingResult result*/){
// Map<String,String> map = new HashMap<>();
// //判断是否含有校验失败
// if(result.hasErrors()){
// //1.获取校验的错误信息
// result.getFieldErrors().forEach((item)->{
// //FieldError 获取错误提示信息
// String defaultMessage = item.getDefaultMessage();
// //获取错误的属性名称
// String field = item.getField();
// map.put(field,defaultMessage);
// });
// return R.error(400,"提交的数据不合法").put("data",map);
// }
// else{
// brandService.save(brand);
// }
/**
* 将数据校验出现的异常放到统一异常处理中处理
*/
brandService.save(brand);
return R.ok();
}
注意,如果属性的校验注解中没有写groups属性,则默认为在分组校验的情况下不会进行校验