SpringBoot使用jsr303校验

guomz

依赖添加

<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请求产生的异常如下(校验方式与接受json基本一致):

/**
     * 捕获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);
    }

对于使用错误的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;

一定要注意几个异常类的依赖是否正确。

重要说明

在Spring5.3版本及以后MethodArgumentNotValidException变为BindException的子类。
想细致了解@Valid@Validated的区别的读者可以参考下面的文章:
详解SptingBoot参数校验机制,使用校验不再混乱

阅读 1.4k

不求做完人,只求做凡人。

10 声望
0 粉丝
0 条评论

不求做完人,只求做凡人。

10 声望
0 粉丝
文章目录
宣传栏