统一处理异常
后端
1、创建异常枚举类
通用模块:gulimall-common
BizCodeEnum.java
package com.indi.common.enums;
@Getter
@AllArgsConstructor
public enum BizCodeEnum {
// 通用异常
UNKOWN_EXCEPTION(10000,"系统未知异常"),
VAILID_EXCEPTION(10001,"参数格式校验失败");
private int code; // 响应状态码
private String message; // 响应信息
}
2、创建异常处理类
通用模块:gulimall-common
UnifiedExceptionHandler.java
package com.indi.common.exception;
@Slf4j
@RestControllerAdvice
public class UnifiedExceptionHandler {
/**
* 处理参数异常
*
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e) {
log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass());
BindingResult bindingResult = e.getBindingResult();
Map<String, String> map = new HashMap<>();
bindingResult.getFieldErrors().forEach(fieldError -> {
map.put( fieldError.getField(),fieldError.getDefaultMessage());
});
return R.error(BizCodeEnum.VAILID_EXCEPTION.getCode(), BizCodeEnum.VAILID_EXCEPTION.getMessage()).put("data", map);
}
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable) {
return R.error(BizCodeEnum.UNKOWN_EXCEPTION.getCode(), BizCodeEnum.UNKOWN_EXCEPTION.getMessage());
}
}
3、启动类上添加扫描
商品服务:gulimall-product
GulimallProductApplication.java
4、开启校验功能
商品服务:gulimall-product
AdminBrandController.java
此处以校验参数异常为例,不需要针对异常再做任何处理,只需要开启校验功能@Valid
即可
public R save(@Valid @RequestBody BrandEntity brand) {
// ...
}
JSR303校验
自定义校验注解
创建注解
通用模块:gulimall-common
ListValue.java
package com.indi.common.valid;
@Documented
@Constraint(validatedBy = ListValueConstraintValidator.class) // 使用哪个校验器进行校验,这里不指定,就需要在初始化时指定
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) // 注解的标注位置
@Retention(RUNTIME) // 注解的时机,表示在运行时获取到
public @interface ListValue {
// 想要自定义校验注解,必须包含以下3个属性
String message() default "{com.indi.common.valid.ListValue.message}"; // 错误信息的位置
Class<?>[] groups() default { }; // 需要支持分组校验的功能
Class<? extends Payload>[] payload() default { }; // 可以自定义一些负载信息
int[] values() default {}; // 指定的值
}
创建校验器
通用模块:gulimall-common
ListValueConstraintValidator.java
package com.indi.common.valid;
/**
* 校验器
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
private Set<Integer> set = new HashSet<>();
/**
* 初始化方法
*
* @param constraintAnnotation
*/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] values = constraintAnnotation.values();
if (values != null && values.length > 0) {
for (int value : values) {
set.add(value);
}
}
}
/**
* 判断是否校验成功
*
* @param value 需要校验的值
* @param context 整个校验的上下文环境
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
创建异常提示
通用模块:gulimall-common
ValidationMessages.properties
com.indi.common.valid.ListValue.message=显示状态只能是0、1
使用
商品服务:gulimall-product
BrandEntity.java
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(values = {0, 1})
private Integer showStatus;
分组校验
不指定分组的注解,在分组模式下,不会启用
实体类上添加校验
商品服务:gulimall-product
BrandEntity.java
/**
* 品牌id
*/
@NotNull(message = "修改必须指定品牌", groups = UpdateGroup.class)
@Null(message = "新增不能指定id", groups = AddGroup.class)
@TableId
private Long brandId;
/**
* 品牌名√
*/
@NotBlank(message = "品牌名不能为空", groups = {AddGroup.class, UpdateGroup.class})
private String name;
/**
* 品牌logo地址√
*/
@NotBlank(message = "logo地址不能为空", groups = {AddGroup.class, UpdateGroup.class})
private String logo;
/**
* 介绍
*/
@NotBlank(message = "品牌介绍不能为空", groups = {AddGroup.class, UpdateGroup.class})
private String descript;
/**
* 显示状态[0-不显示;1-显示]
* <p>
* 需要为其单独添加一个组,只在更新显示状态用
* 如果不单独指定,与update使用一个的话,只更新显示状态,不更新其它字段,会触发其它不允许为空的校验
*/
@NotNull(message = "显示状态只能为0、1", groups = {AddGroup.class, UpdateGroup.class, UpdateStatusGroup.class})
@ListValue(values = {0, 1}, groups = {AddGroup.class, UpdateGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
/**
* 检索首字母
*/
@Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母", groups = {AddGroup.class, UpdateGroup.class})
private String firstLetter;
/**
* 排序
*/
@NotNull(message = "排序必须为正整数", groups = {AddGroup.class, UpdateGroup.class})
@Min(value = 0, message = "排序必须为正整数", groups = {AddGroup.class, UpdateGroup.class})
private Integer sort;
/**
* 更新状态的分组
*/
public interface UpdateStatusGroup { }
创建分组
通用模块:gulimall-common
valid包下创建两个接口:AddGroup.java
、UpdateGroup.java
一个用来添加、一个用来更新
开启校验
商品服务:gulimall-product
AdminBrandController.java
1、新增品牌
2、更新品牌
3、更新显示状态
如果更新显示状态和更新品牌都用update分组的话,只修改状态的时候,没有品牌名,就会报错,所以需要单独为显示状态创建一个分组,然后修改的接口还要单独再一遍,但是里面的内容不变,就是变了一下请求地址跟启用的分组,不知道以后有没有别的校验方法,总感觉这个很麻烦。
/**
* 保存
*/
@ApiOperation("添加品牌")
@PostMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}
/**
* 修改
*/
@ApiOperation("修改品牌")
@PostMapping("/update")
public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand) {
brandService.updateById(brand);
return R.ok();
}
/**
* 修改状态
*/
@ApiOperation("修改状态")
@PostMapping("/updateStatus")
public R updateStatus(@Validated(UpdateStatusGroup.class) @RequestBody BrandEntity brand) {
brandService.updateById(brand);
return R.ok();
}