常規校驗
@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;
}
}
測試
提示了錯誤信息,以及每個屬性的校驗失敗信息,可以根據需要調整是否顯示或記錄到日誌中。