自定义注解实现参数校验
一.定义注解类
/**
* 用户验证状态是否在指定范围内的注解
*/
@Documented
// JVM会读取注解,同时会保存到class文件中
@Retention(RetentionPolicy.RUNTIME)
// 用于字段或者方法参数
@Target({ElementType.FIELD,ElementType.PARAMETER})
// 指定检验处理类, 可以指定多个
@Constraint(validatedBy = FlagValidatorClass.class)
public @interface FlagValidator {
// 校验参数是否在某个int 数组中
int[] value() default {};
// 出现检验失败的错误消息
String message() default "flag is not found";
// 分组
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
二.定义校验类
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* 状态标记校验器 校验 FlagValidator 注解的地方
*
* ConstraintValidator<FlagValidator, Integer>
* FlagValidator : 校验的注解
* Integer : 前端传的值
*/
public class FlagValidatorClass implements ConstraintValidator<FlagValidator, Integer> {
private int[] values;
// 初始化方法
@Override
public void initialize(FlagValidator flagValidator) {
// 获取到 设置了 FlagValidator 注解参数中的 value 范围
this.values = flagValidator.value();
}
// 校验
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
boolean isValid = false;
// 如果参数没有传值则默认是true, 不做检验
if(value==null){
//当状态为空时使用默认值
return true;
}
// 否则遍历改参数的 value 看参数值是否在范围内
for (int item : values) {
if (item == value) {
isValid = true;
break;
}
}
return isValid;
}
}
如果又需要对Double类型的参数进行类似的校验,则直接复制上面的类,修改类型的范型即可, 这样就会根据参数的类型去找相应的校验器
public class FlagValidatorClass implements ConstraintValidator<FlagValidator, Double> {
...
}
三.统一异常处理
//如果返回的为json数据或其它对象,添加该注解
@RestControllerAdvice
//@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 前端参数提交是json格式时校验抛出的异常
* json格式提交时,采用json数据的数据转换器进行处理,错误时抛出MethodArgumentNotValidException异常
*/
// 拦截对应的异常
@ExceptionHandler(MethodArgumentNotValidException.class)
// 指明 400 异常码
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result handlerNotValidException(MethodArgumentNotValidException exception) throws Exception {
return handlerNotValidException(exception);
}
/**
* 仅对于表单提交有效
* 如果是表单类型的提交,进行参数校验错误时会抛出BindException异常
*/
// 拦截对应的异常
@ExceptionHandler(BindException.class)
// 指明 400 异常码
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result handlerBindException(BindException exception) {
return handlerNotValidException(exception);
}
private ResultDTO handlerNotValidException(Exception e){
BindingResult result;
if (e instanceof BindException) {
BindException exception = (BindException) e;
// 获取验证失败的结果
result = exception.getBindingResult();
} else {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
// 获取验证失败的结果
result = exception.getBindingResult();
}
StringBuilder errorMsg =new StringBuilder("校验失败:");
for (FieldError fieldError : result.getFieldErrors()) {
errorMsg.append(fieldError.getDefaultMessage()).append(", ");
}
// 这里自定义了一个异常码的枚举,其实值就是一个状态码字符串
//return Result.fail(CoreConstants.E_PARAMETER,errorMsg.toString());
return Result.fail("5002",errorMsg.toString());
}
}
其中Result是返给前端的封装类
public class Result<T extends Object>{
private static final long serialVersionUID = -7387542509934814087L;
// 成功标志
private boolean success;
// 状态码
private String code;
// 异常消息
private String message;
// 数据
private T data;
// xxx 这里一些方法省略写了
public static ResultDTO fail(String code, String msg) {
ResultDTO result = new ResultDTO(false, null,code, msg);
return result;
}
public ResultDTO(boolean success, Object data, String code, String message) {
this.data = (T) data;
this.success = success;
this.code = code;
this.message = message;
}
}
四.效果图
这里的message格式可以根据自己的需求进行改造