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;
    }
}

測試

在這裏插入圖片描述

在這裏插入圖片描述
提示了錯誤信息,以及每個屬性的校驗失敗信息,可以根據需要調整是否顯示或記錄到日誌中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章