天空和我的中间,只剩下倾盆的思念
简单校验
使用示例:
-
引入对应的校验依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
-
在需要校验的业务字段上添加相应的注解,如:
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名必须提交")
private String name;
/**
* 品牌logo地址
*/
@NotEmpty
@URL(message = "logo必须是一个合法的url地址")
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty
@Pattern(regexp = "/^[a-zA-Z]$/", message = "检索首字母必须是一个字母")
private String firstLetter;
/**
* 排序
*/
@NotNull
@Min(value = 0, message = "排序必须大于等于0")
private Integer sort;
}
-
在需要校验的方法上(一般是controller方法)添加
@Valid
注解,在后面追加BindingResult
类,相当于错误结果都追加到这个类中
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
if (result.hasErrors()){
Map<String, String> map = new HashMap<>();
//1、获取校验的结果
result.getFieldErrors().forEach((item)->{
//获取到错误提示
String message = item.getDefaultMessage();
//获取到错误属性的名字
String field = item.getField();
map.put(field, message);
});
return R.error().put("data", map);
}else{
brandService.save(brand);
}
return R.ok();
}
统一异常处理
使用示例:
-
定义特定错误的状态码,common模块中单独定义一个常量类,用来存储这些错误状态码
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*/
public enum BizCodeEnum {
UNKNOW_EXEPTION(10000,"系统未知异常"),
VALID_EXCEPTION( 10001,"参数格式校验失败");
private int code;
private String msg;
BizCodeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
-
在product里面新建异常拦截类
GulimallExceptionControllerAdvice
,用来集中处理所有异常@ExceptionHandler
@RestControllerAdvice(basePackages = "com.xmh.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
// 处理数据校验异常
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
Map<String, String> errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach((fieldError)->{
errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
});
return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(),BizCodeEnume.VALID_EXCEPTION.getMsg()).put("data", errorMap);
}
//处理全局异常
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
return R.error(BizCodeEnume.UNKNOW_EXEPTION.getCode(), BizCodeEnume.UNKNOW_EXEPTION.getMsg());
}
}
思考:这样可以做到单个模块的全局异常拦截,但是每一个模块都要单独定义一个感觉不优雅。
改进:可不可以在common模块定义一个所有模块能共用的,后面要用的话,直接在每一个controller类继承这个公共类即可。如:
public class BaseController
{
public static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class);
/**
* 统一异常处理
* @param request
* @param response
* @param exception
*/
@ExceptionHandler
public R exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception)
{
LOGGER.error("统一异常处理:", exception);
request.setAttribute("ex", exception);
if (null != request.getHeader("X-Requested-With")
&& request.getHeader("X-Requested-With").equalsIgnoreCase("XMLHttpRequest"))
{
request.setAttribute("requestHeader", "ajax");
}
// 业务异常捕获
if (exception instanceof NormalRuntimeException)
{
exception.printStackTrace();
return R.fail("操作失败!" + exception.getMessage());
}
// 自定义异常捕获
if (exception instanceof CustomException)
{
exception.printStackTrace();
return R.fail(exception.getMessage());
}
return R.fail("未知异常!请稍后再试或联系管理员!");
}
}
分组校验
使用示例:
-
在common中新建valid包,里面新建两个空接口
AddGroup
,UpdateGroup
用来分组
-
在需要分组校验的字段上标注分组,如:
@NotEmpty
@NotBlank(message = "品牌名必须非空",groups = {UpdateGroup.class,AddGroup.class})
private String name;
-
校验方法上的
@Valid
注解换成@Validated
注解,并标记好分组
@RequestMapping("/save")
public R save(@Valided({AddGroup.class}) @RequestBody BrandEntity brand){
...
}
自定义校验
使用示例:
-
编写一个自定义注解接口,如:
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class }) //指定校验器 这里指定我们自定义的ListValueConstraintValidator
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
// 使用我们自己定义的错误信息 在common模块下的resources中
// String message() default "{com.atguigu.common.valid.ListValue.message}";
String message() default "值必须是0或者1";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
//预先准备的值 vals={0,1}
int[] vals() default {};
}
-
编写一个自定义注解校验器,如
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set=new HashSet<>();
@Override
public void initialize(ListValue constraintAnnotation) {
int[] value = constraintAnnotation.vals();
for (int i : value) {
set.add(i);
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
-
关联检验接口与校验器
@Constraint(validatedBy = {ListValueConstraintValidator.class})
-
在需要的字段上进行添加
@ListValue(vals = {0, 1},groups = {AddGroup.class, UpdateGroup.class})标签:return,数字,JSR303,校验,private,class,public,String From: https://www.cnblogs.com/l12138h/p/16707996.html
private Integer showStatus;