Skip to content

参数校验

POM依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

注解说明

注解说明
@AssertTrue可以为null,如果不为null的话必须为true
@AssertFalse可以为null,如果不为null的话必须为false
@DecimalMax设置不能超过最大值,适用于浮点数
@DecimalMin设置不能超过最小值,适用于浮点数
@Max最大不得超过此最大值,适用于整数
@Min最小不得小于此最小值,适用于整数
@NotNull不能为null
@Null必须为null
@NotBlank字符串不能为null,字符串trim()后也不能等于“”
@Length长度必须在指定范围内,一般用于限定字符串的长度
@NotEmpty不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
@URL必须是一个URL
@Email必须是email格式
@Size集合、数组、map等的size()值必须在指定范围内
@Pattern必须满足指定的正则表达式

使用方式

  1. 将注解写在实体类的字段上
java
@Data
public class UserVO implements Serializable {
 
    @Serial
    private static final long serialVersionUID = 1L;
 
    @NotNull(message = "用户ID不能为空")
    private Long userId;
 
    @NotBlank(message = "用户账号不能为空")
    private String username;
 
    @NotEmpty(message = "角色不能为空")
    private Set<Long> roleIds;
 
}
  1. 在Controller上增加校验注解
java
@RestController
@RequestMapping("/basic/user")
@Tag(name = "用户管理")
public class UserController {
 
    @PostMapping("/addUser")
    @Operation(summary = "添加用户")
    public ResponseEntity<Void> addUser(@RequestBody @Validated UserVO userVO) {
        userService.addUser(userVO);
        return ResponseEntity.ok().build();
    }
 
}

Controller单参数校验

java
//注意:单参数校验需要在controller类上添加@Validated注解才能生效
@Validated
@RestController
@RequestMapping("/basic/user")
@Tag(name = "用户管理")
public class UserController {
 
    @DeleteMapping("/deleteUserByUserId")  
    @Operation(summary = "删除用户 -> 根据用户ID删除")
    public ResponseEntity<Void> deleteUserByUserId(@RequestParam("userId") @NotNull(message = "用户ID不能为空") Long userId) {
        userService.deleteUserByUserId(userId);
        return ResponseEntity.ok().build();
    }
 
}

分组校验

说明:新增的时候不需要传ID,修改的时候才传ID,但新增和修改用了同一个实体类,这时候就需要对id字段做分组校验。

  1. 定义分组标记
java
//自定义新增校验,写一个空接口标识即可
public interface Add {
}
 
//自定义修改校验
public interface Update {
}
  1. 实体类中增加分组校验注解
java
@Data
public class UserVO implements Serializable {
 
    @Serial
    private static final long serialVersionUID = 1L;
 
    //修改时不能为空
    @NotNull(message = "用户ID不能为空", groups = {Update.class})
    private Long userId;

    //新增和修改时不能为空
    @NotBlank(message = "用户账号不能为空", groups = {Add.class, Update.class})
    private String username;
 
    @NotEmpty(message = "角色不能为空")
    private Set<Long> roleIds;
 
}
  1. Controller中添加分组校验注解 需要注意:如果Controller加了@Validated(Add.class)分组校验,那么实体类中参数校验注解的groups就必须对应,否则就不会校验,包括没加groups的。比如这里指定的是Add.class,那么上面UserVO实体中的roleIds字段虽然加了@NotEmpty注解但没加groups属性,所以不会被校验。
java
@PostMapping("/addUser")
@PreAuthorize("hasAuthority('basic:user:add')")
@Operation(summary = "添加用户")
public ResponseEntity<Void> addUser(@RequestBody @Validated(Add.class) UserVO userVO) {
    userService.addUser(userVO);
    return ResponseEntity.ok().build();
}

手工校验

java
public class ValidateUtil {
    public static <T> void valid(T t) {
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        Set<ConstraintViolation<T>> errors = validator.validate(t);
        List<String> errorMsg = errors.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(errorMsg)) {
            StringJoiner sj = new StringJoiner(",");
            errorMsg.forEach(sj::add);
            throw new BusinessException(sj.toString());
        }
    }
}

参数校验的异常处理

java
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
 
    /**
     * 参数异常
     * @param e 异常
     * @return 响应
     */
    @ExceptionHandler(value = {IllegalArgumentException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class})
    public ResponseEntity<Object> paramsException(Exception e) {
        if (e instanceof IllegalArgumentException) {
            log.warn(e.getMessage(), e);
            return ResponseEntity.status(RespEnum.PARAM_ERROR.getCode()).body(RespEnum.PARAM_ERROR.getDesc());
        } else if (e instanceof MethodArgumentNotValidException) {
            //参数异常处理
            log.warn(e.getMessage(), e);
            BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            List<FieldError> errors = bindingResult.getFieldErrors();
            StringJoiner sj = new StringJoiner(",");
            errors.forEach(error -> sj.add(error.getDefaultMessage()));
            return ResponseEntity.status(RespEnum.PARAM_ERROR.getCode()).body(sj.toString());
        } else if (e instanceof ConstraintViolationException) {
            //参数异常处理
            log.warn(e.getMessage(), e);
            Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) e).getConstraintViolations();
            StringJoiner sj = new StringJoiner(",");
            constraintViolations.forEach(c -> sj.add(c.getMessageTemplate()));
            return ResponseEntity.status(RespEnum.PARAM_ERROR.getCode()).body(sj.toString());
        } else {
            return exception(e);
        }
    }
 
    /**
     * 未知异常
     * @param e 异常
     * @return 响应
     */
    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity<Object> exception(Exception e) {
        //记录日志
        log.error(e.getMessage(), e);
        //响应 系统异常
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(RespEnum.FAILED.getDesc());
    }
 
}