前端防君子,后端防小人
前端做了数据校检以后,为了保证数据的正确性,需要在后端进行校检。
- 使用JSR303进行数据校检
javax.validation.constraints
第一步需要在需要校检的实体类的属性中添加注解
@Null
@TableId
private Long brandId;
第二布在方法参数中添加@Valid注解
public R save(@Valid @RequestBody BrandEntity brand)
可以在属性注解中添加自己的数据提示;
- 校验数据是否出错没出错可以自定义在方法属性后添加一个属性BindingResult result就可以
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
//判断result是否错误
if (result.hasErrors()){
Map<String,String> map = new HashMap<>();
//获取校监错误数据
result.getFieldErrors().forEach((item)->{
//获取错误提示
String message = item.getDefaultMessage();
//获取错误属性
String field = item.getField();
map.put(field,message);
});
return R.error(400,"提交的数据不合法").put("data",map);
}else {
brandService.save(brand);
}
//不处理异常,只考虑正确情况
return R.ok();
只不过这种情况不太适合企业开发环境,所以我们可以创建一个包,在此包下集中处理异常
/*
*
* 集中处理所有异常
* */
@Slf4j
//统一处理异常
//@ResponseBody 1
//@ControllerAdvice(basePackages ="com.atguli.gulimall.product.controller") 2
@RestControllerAdvice(basePackages = "com.atguli.gulimall.product.controller") 1 2注解在此注解中都包含
public class GulimallExceptionControllerAdvice {
//精准异常MethodArgumentNotValidException
@ResponseBody
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException exception){
//需要获取准确异常类型可以通过Exception来获取将属性类型和注解属性类型全部设置为exception即可
log.error("数据检验出现问题:{},异常类型:{}",exception.getMessage(),exception.getClass());
Map<String,String> map = new HashMap<>();
//获取到错误信息
BindingResult bindingResult = exception.getBindingResult();
//获取到当前文件的错误信息
bindingResult.getFieldErrors().forEach((item)->{
//获取到当前错误的文件
String field = item.getField();
//获取到当前文件的错误提示
String defaultMessage = item.getDefaultMessage();
map.put(field,defaultMessage);
});
return R.error(400,"数据校检错误").put("data",map);
}
}
异常信息都有错误状态码,我们可以将他们写为枚举类型
public enum BizCodeEnume {
//枚举间隔为,结尾不为;
UNKNOW_EXCEPTION(10000,"系统未知异常"),
VAILD_EXCEPTION(10001,"参数格式校检失败");
private int code;
private String msg;
BizCodeEnume(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
此处全局异常处理类可以修改为
@Slf4j
//统一处理异常
@RestControllerAdvice(basePackages = "com.atguli.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
//精准
@ResponseBody
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException exception){
log.error("数据检验出现问题:{},异常类型:{}",exception.getMessage(),exception.getClass());
Map<String,String> map = new HashMap<>();
//获取到错误信息
BindingResult bindingResult = exception.getBindingResult();
//获取到当前文件的错误信息
bindingResult.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);
}
//全局
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}
数据校验分组功能在新增和修改时,需要的参数不同就需要有不同的检验规则,这时候就可以使用分组校检功能
可以创建两个空接口,不需要实现
新增
public interface AddGroup {
}
修改
public interface UpdataGroup {
}
实体类
@NotNull(message = "修改品牌id不能为空",groups = {UpdataGroup.class,UpdateStatusGroup.class})
@Null(message = "新增品牌id必须为空",groups = {AddGroup.class})
@TableId
private Long brandId; id属性添加不需要,数据库会自动添加,在修改时就需要id属性了
方法修改
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand/*)
升级版本
自定义校检功能
1编写自定义校检注解
@ListValue
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {ListValueValidationMessages.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "{com.atguli.common.valid.ListValue.message}";//当前类的地址
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default { };
}
导入依赖
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
在properties配置包下添加一个ValidationMessages.properties文件
com.atguli.common.valid.ListValue.message=必须提交指定的值
2.编写自定义校检器
public class ListValueValidationMessages implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
//初始化
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
//判断是否检验成功
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
3.关联自定义的校检注解和校检器
@Constraint(validatedBy = {ListValueValidationMessages.class})//在自定义注解中添加此注解可以指定多个校检器
完结撒花