SpringBoot注解校验请求参数

常规校验

    @RequestMapping(
            value = "test",
            method = RequestMethod.POST)
    public FrogTest test(@RequestBody FrogTest bean) {
        if (bean.getName() == null) {
            throw new MyException("A02", "名称不能为空");
        }
        if (bean.getSize() > 100) {
            throw new MyException("A03", "size值超过允许最大值");
        }
        return bean;
    }

通常通过以上方式,对前端请求参数进行校验,如果请求bean(FrogTest)中需要校验的属性太多时,对每个参数写一个对应的if判断,代码可读性很差,所以可以在请求对象属性加上注解来校验其合法性。

属性加校验注解

public class FrogTest implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @NotEmpty(message = "名称项不能为空")
    private String name;

    private String describ;

    private LocalDateTime date;

    @NotEmpty(message = "状态标识项不能为空")
    @Pattern(regexp = "[012]", message = "无效的状态标志")
    private String state;

    @SizeValidator(maxSize = 100.1, message = "值太大")
    private Double size;
    ...
    省略getter和setter
}

其中@NotEmpty和@Pattern是javax.validation.constraints包中注解,前者表示不为空,后者表示为特定格式,现在表示只能为0、1、2。@SizeValidator是自定义注解,用来校验值得大小(可以用@Size),如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
        validatedBy = {SizeValidatorImpl.class}
)
public @interface SizeValidator {

    double maxSize() default 10000.0;

    String message() default "值得大小不合法";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

约束实现类:

public class SizeValidatorImpl implements ConstraintValidator<SizeValidator, Double> {

    private double maxSize;

    @Override
    public void initialize(SizeValidator constraintAnnotation) {
        maxSize = constraintAnnotation.maxSize();
    }

    @Override
    public boolean isValid(Double value, ConstraintValidatorContext context) {
        if (value > 0 && value < maxSize) {
            return true;
        }
        return false;
    }
}

修改Controller

请求参数增加@Valid注解

    @RequestMapping(
            value = "test",
            method = RequestMethod.POST)
    public FrogTest test(@RequestBody @Valid FrogTest bean) {
        /*不需要通过if判断校验
        if (bean.getName() == null) {
            throw new MyException("A02", "名称不能为空");
        }
        if (bean.getSize() > 100) {
            throw new MyException("A03", "size值超过允许最大值");
        }*/
        return bean;
    }

异常拦截与返回

在SpringBoot项目通用返回值与全局异常处理的基础上修改

@RestControllerAdvice
public class MyExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e, HttpServletRequest request, HttpServletResponse response){

        logger.error("请求:{}发生异常:{}", request.getRequestURI(), e);

        Result result = new Result();
        result.setSuccess(false);
        if (e instanceof MyException) {
            result.setErrorCode(((MyException) e).getErrorCode());
            result.setErrorMsg(((MyException) e).getErrorMsg());
        } else {
            result.setErrorCode(MyExceptionEnum.SYSTEM_ERROR.getCode());
            result.setErrorMsg(MyExceptionEnum.SYSTEM_ERROR.getMsg());
        }

        return result;
    }

    /**
     * 自定义注解异常拦截
     */
    @ExceptionHandler({BindException.class, ConstraintViolationException.class, MethodArgumentNotValidException.class})
    public Object handleMethodArgumentNotValidException(Exception e, HttpServletRequest request) {

        logger.error("请求:{}发生异常:{}", request.getRequestURI(), e);

        // 错误信息
        StringBuilder sb = new StringBuilder("参数校验失败:");
        // 错误信息map
        Map<String, String> error = new HashMap<>();

        String msg = "";
        if (!(e instanceof BindException) && !(e instanceof MethodArgumentNotValidException)) {
            for (ConstraintViolation cv: ((ConstraintViolationException)e).getConstraintViolations()) {
                msg = cv.getMessage();
                sb.append(msg).append(";");

                Iterator<Path.Node> it = cv.getPropertyPath().iterator();
                Path.Node last = null;
                while (it.hasNext()) {
                    last = (Path.Node)it.next();
                }
                /*for(last = null; it.hasNext(); last = (Path.Node)it.next()) {
                }*/
                error.put(last != null ? last.getName() : "", msg);
            }
        } else {
            List<ObjectError> allErrors = null;
            if (e instanceof BindException) {
                allErrors = ((BindException)e).getAllErrors();
            } else {
                allErrors = ((MethodArgumentNotValidException)e).getBindingResult().getAllErrors();
            }
            // 拼接错误信息
            for (ObjectError oe : allErrors) {
                msg = oe.getDefaultMessage();
                sb.append(msg).append(";");
                if (oe instanceof FieldError) {
                    error.put(((FieldError)oe).getField(), msg);
                } else {
                    error.put(oe.getObjectName(), msg);
                }
            }
        }

        Result<Map> result = new Result<>(error);
        result.setSuccess(false);
        result.setErrorCode(MyExceptionEnum.REQUESTPARAM_ERROR.getCode());
        result.setErrorMsg(MyExceptionEnum.REQUESTPARAM_ERROR.getMsg() + sb.toString());
        return result;
    }
}

测试

SpringBoot注解校验请求参数_第1张图片

SpringBoot注解校验请求参数_第2张图片
提示了错误信息,以及每个属性的校验失败信息,可以根据需要调整是否显示或记录到日志中。

你可能感兴趣的:(SpringBoot)