依赖添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
一些较老版本的SpringBoot需要添加相关依赖,我使用的2.1.4发行版不用这个操作。
验证使用对象接收参数的情况(SpringMvc校验)
public class SetRequest {
private Long id;
@NotBlank(message = "姓名为空")
private String name;
@Valid
@NotNull(message = "详情信息为空")
private InfoSetRequest info;
}
首先在需要验证的对象的对应字段上方加上校验注解。如果类中包含其他类属性需要校验,则在属性上加@Valid
注解后再加校验注解,通常是加@NotNull
先判断对象属性是否为空,属性内部字段校验则在该类内部进行注解的添加。以下为一些常用注解:
@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
@RequestMapping(value = "/deduct", method = RequestMethod.POST)
public BusinessResponse setPointDeduct(@RequestBody @Valid PointDeductSetRequest request){
pointDeductService.setPointDeductRule(request);
return new BusinessResponse(ResponseEnum.OK);
}
之后在controller方法的对象参数前加@Valid注解。
此处也可以使用@Validated
注解,并添加分组参数。
对象参数的分组校验
public interface Insert {
}
public interface Update {
}
分组校验首先需要以接口的形式建立分组,比如可以根据插入与更新分别建立对应分组接口。
@Data
public class SetRequest {
@NotNull(message = "主键为空", groups = {Update.class})
private Long id;
@NotBlank(message = "名称为空", groups = {Insert.class,Update.class})
private String name;
}
在需要校验的类属性的校验注解中完善groups参数,可以传入多个分组接口。
@RequestMapping(value = "/insert", method = RequestMethod.POST)
public Responses addOilTank(@RequestBody @Validated({Insert.class}) SetRequest request){
myService.insert(request);
return new Responses(ResponseEnum.OK);
}
@RequestMapping(value = "/update", method = RequestMethod.PUT)
public Responses updateOilTank(@RequestBody @Validated({Update.class}) SetRequest request){
myService.update(request);
return new Responses(ResponseEnum.OK);
}
回到controller层,在对象前的@Validated
注解中传入分组接口,同样可以传入多个分组。
校验使用单个参数接受的情况(AOP校验)
@RequestMapping(value = "/deduct", method = RequestMethod.GET)
public PageResponse<TPointDeduct> getPointDeductList(@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "pageSize", required = false) Integer pageSize,
@RequestParam(value = "tenantId", required = false) @NotBlank(message = "租户id为空") String tenantId,
@RequestParam(value = "status", required = false) Integer status){
PageResponse<TPointDeduct> response = pointDeductService.getPointDeductList(page, pageSize, tenantId, status);
response.setCodeMsg(ResponseEnum.OK);
return response;
}
首先需要在controller类上加@Validated
注解,之后在方法中需要校验的参数前加上对应的校验注解进行校验。
对校验产生的异常的捕获
定义全局异常处理类并用@ControllerAdvice
标注,由于对象和单个参数因校验产生的异常类型不同,因此需要分别处理。下面两个例子中的Response类为我自己定义的返回结构,不是框架的部分。
对于对象作为接收前端json请求的情况,因校验产生的异常类型为MethodArgumentNotValidException
,示例方法如下:
/**
* 捕获303对于body中的对象字段校验(MVC校验)
* @param e
* @param request
* @return
*/@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request){
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
if (fieldErrors != null && !fieldErrors.isEmpty()){
String message = fieldErrors.get(0).getDefaultMessage();
log.error("URL : {}", request.getRequestURL().toString());
log.error("HTTP Method : {}", request.getMethod());
log.error(message, e);
}
HttpStatus httpStatus = HttpStatus.FORBIDDEN;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Response response = new Response();
response.setCode(ResponseEnum.FORMAT_ERROR.code());
response.setMessage(ResponseEnum.FORMAT_ERROR.message());
return new ResponseEntity<>(response, headers, httpStatus);
}
对于使用单个参数接受前端请求,因校验产生的异常类为ConstraintViolationException
,示例方法如下:
/**
* 捕获303对于request param单个参数的校验(AOP校验)
* @param e
* @param request
* @return
*/@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request){
HttpStatus httpStatus = HttpStatus.FORBIDDEN;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
log.error("URL : {}", request.getRequestURL().toString());
log.error("HTTP Method : {}", request.getMethod());
log.error(e.getMessage());
Response response = new Response();
response.setCode(ResponseEnum.FORMAT_ERROR.code());
response.setMessage(ResponseEnum.FORMAT_ERROR.message());
return new ResponseEntity<>(response, headers, httpStatus);
}
使用对象接收get请求或者post请求表单数据使用对象接收时产生的异常如下:
/**
* 捕获303对于get请求对象字段校验(MVC校验)
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(BindException.class)
@ResponseBody
ResponseEntity<Object> handleBindException(BindException e, HttpServletRequest request) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
if (fieldErrors != null && !fieldErrors.isEmpty()) {
String message = fieldErrors.get(0).getDefaultMessage();
log.error("URL : {}", request.getRequestURL().toString());
log.error("HTTP Method : {}", request.getMethod());
logger.error(message, e);
}
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Response response = new Response();
response.setCode(ResponseEnum.FORMAT_ERROR.code());
response.setMessage(ResponseEnum.FORMAT_ERROR.message());
return new ResponseEntity<>(response, headers, httpStatus);
}
表单请求controller接收校验示例(与接收post请求json数据的区别在于没有@RequestBody
注解):
@PostMapping("/formreq")
public BusinessResponse formRequest(@Validated PostReq postReq){
log.info(postReq.toString());
return new BusinessResponse("ok",123);
}
对于使用错误的http请求方法请求接口产生的异常校验:
/**
* 捕获http方法不支持的异常
* @param e
* @param request
* @return
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
ResponseEntity<Object> handleMethodNotSupportException(HttpRequestMethodNotSupportedException e, HttpServletRequest request){
log.error(e.getMessage());
log.error("URL : {}", request.getRequestURL().toString());
log.error("HTTP Method : {}", request.getMethod());
e.printStackTrace();
HttpStatus httpStatus = HttpStatus.METHOD_NOT_ALLOWED;
HttpHeaders headers = new HttpHeaders();
Response response = new Response();
response.setCode(ResponseEnum.FORMAT_ERROR.code());
response.setMessage(ResponseEnum.FORMAT_ERROR.message());
headers.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(response, headers, httpStatus);
}
一些关键类的依赖:
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
一定要注意几个异常类的依赖是否正确。
对于异常捕获注解@ControllerAdvice的增强
@ControllerAdvice
注解支持对指定包路径下的controller、直接指定controller的class或对于添加了特定注解的controller进行异常捕获,详见https://blog.csdn.net/feyehong/article/details/131028588
重要说明
在Spring5.3版本及以后MethodArgumentNotValidException
变为BindException
的子类。
想细致了解@Valid
与@Validated
的区别的读者可以参考下面的文章:
详解SptingBoot参数校验机制,使用校验不再混乱
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。