参数校验
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格式 | |
@Size | 集合、数组、map等的size()值必须在指定范围内 |
@Pattern | 必须满足指定的正则表达式 |
使用方式
- 将注解写在实体类的字段上
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;
}
- 在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字段做分组校验。
- 定义分组标记
java
//自定义新增校验,写一个空接口标识即可
public interface Add {
}
//自定义修改校验
public interface Update {
}
- 实体类中增加分组校验注解
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;
}
- 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());
}
}