写接口的时候,参数校验是不是还一个一个的判空或者判断格式是否正确,业务代码没写几行,写了一堆if StringUtils.isBlank,对不对,我说的对不对!!!小啦,逼格小啦~!。今天我就来深入浅出的讲讲spring boot+@Validated+@controlleradvice+自定义注解实现统一参数校验。
一、@Validated注解介绍
@Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束。
@Validated作用在类、方法和参数上
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
Class<?>[] value() default {};
}
@Validated的校验注解及其说明
二 @Validated的使用方法
@PostMapping("valid")
public Response valid(@Validated @RequestBody RequestBean requestBean) {
Response response = Response.success();
return response;
}
使用实体类接收body参数,如果非body参数,将校验注解(例如NotBlank)直接写在参数前面。实体类的话就写在属性上,就像下面这样。
public class RequestBean implements Serializable {
@NotBlank(message = "缺少appId")
private String appId;
@NotBlank(message = "缺少requestData")
private String requestData;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getRequestData() {
return requestData;
}
public void setRequestData(String requestData) {
this.requestData = requestData;
}
}
@Validated如果没有出现校验失败,则会抛出MethodArgumentNotValidException,这个时候我们可以用统一异常管理类,来组装异常信息返回给前台。操作如下。
我们用统一异常管理类捕获参数校验异常,因为是写demo,所以下面的统一异常处理的不够细粒度,等有机会专门写一篇文章用来介绍统一异常管理。
@ControllerAdvice
public class GlobalExceptionHandler {
private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s";
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ResponseBody
@ExceptionHandler({NullPointerException.class, RuntimeException.class, ClassCastException.class,
IOException.class, IndexOutOfBoundsException.class, HttpRequestMethodNotSupportedException.class})
public Response nullPointerExceptionHandler(NullPointerException ex) {
return responseFormat(ResponseEnum.FAIL.getCode(), ex);
}
/**
* @param exception
* @return
* @throws Exception
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Response MethodArgumentNotValidHandler(
MethodArgumentNotValidException exception) {
//按需重新封装需要返回的错误信息
List<ArgumentInvalidResponse> invalidArguments = new ArrayList<ArgumentInvalidResponse>();
//解析原错误信息,封装后返回,此处返回非法的字段名称,原始值,错误信息
for (FieldError error : exception.getBindingResult().getFieldErrors()) {
ArgumentInvalidResponse invalidArgument = new ArgumentInvalidResponse();
invalidArgument.setErrorMessage(error.getDefaultMessage());
invalidArgument.setField(error.getField());
invalidArgument.setRejectedValue(error.getRejectedValue());
invalidArguments.add(invalidArgument);
}
return Response.fail(ResponseEnum.PARAMETER_ERROR.getCode(), ResponseEnum.PARAMETER_ERROR.getMessage(), invalidArguments);
}
private <T extends Throwable> Response<Object> responseFormat(Integer code, T ex) {
ex.printStackTrace();
logger.error(String.format(logExceptionFormat, code, ex.getMessage()));
String message = String.format(logExceptionFormat, code, ex.getMessage());
return Response.fail(code, message, null);
}
}
创建一个专门用于组装参数校验失败信息的对象
public class ArgumentInvalidResponse {
private String errorMessage;
private String field;
private Object rejectedValue;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public Object getRejectedValue() {
return rejectedValue;
}
public void setRejectedValue(Object rejectedValue) {
this.rejectedValue = rejectedValue;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
三、测试
{
"code": 40005,
"message": "PARAMETER_VALIDATION_FAILED",
"data": [
{
"errorMessage": "缺少requestData",
"field": "requestData"
},
{
"errorMessage": "缺少appId",
"field": "appId"
}
]
}
这样我们就完成了基于注解的参数校验,有的小伙伴可能会说,有的字段框架自带的注解不满足日常需求,比如我想校验身份证,手机号等等怎么办,那就得用到自定义校验注解了,这个我们下次再写。
有问题的在下方留言,大家可以关注我的公众号:codesls。