一、前言
在后端项目中,我们在做接口开发的时候,多多少少的,总会遇到需要做参数校验的情况。不同的框架会有不同的处理方式,这里我们整理下spring框架下的参数校验。
主要的校验器有两种
- hibernate-validator
- spring-boot-starter-validation
这两种都是对JSR303的实现,而spring-boot-starter-validation 又是对 hibernate-validator的二次封装。
二、常规使用
@Data
public class Article {
@NotBlank
private String title;
}
@PostMapping
public void create(@Validated @RequestBody Article article){
System.out.println(JSON.toJSONString(article));
}
三、分组校验
@Data
public class Article {
public interface Create{}
public interface Update{}
@NotNull(groups = {Update.class})
private Long id;
@NotBlank(groups = {Create.class,Update.class})
private String title;
@NotBlank
private String content;
}
@PostMapping
public void create(@Validated(value = {Article.Create.class}) @RequestBody Article article){
System.out.println(JSON.toJSONString(article));
}
四、嵌套校验
嵌套的属性上要加上@Valid才会生效
@Data
public class Article {
@NotBlank
private String title;
@Valid
@NotNull
private Tag tag;
}
五、单个参数的校验
如果参数不是对象,而是一个属性,这个时候需要把 @Validated 加在类上
@Validated
@RestController
@RequestMapping
public class ArticleController {
@GetMapping(value = "one")
public void query(
@Min(value = 10) @RequestParam(value = "age") Integer age
){
}
}
六、自定义校验
注解
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UserNameConstraintValidator.class)
@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface UserName {
String message() default "username is invalid";
Class<?>[] groups() default {};
int min();
Class<? extends Payload>[] payload() default { };
}
对应的验证器
public class UserNameConstraintValidator implements ConstraintValidator<UserName, String> {
private Integer min = 0;
@Override
public void initialize(UserName constraintAnnotation) {
min = constraintAnnotation.min();
}
@Override
public boolean isValid(String o, ConstraintValidatorContext constraintValidatorContext) {
return StringUtils.hasText(o) && o.length()>min ;
}
}
只要注解加在属性上即可。
七、全局异常捕获
参数校验失败之后,还需要告知客户端端,参数校验失败,这个时候可以做全局异常捕获来处理
@Slf4j
@ControllerAdvice
@ResponseBody
public class ApiExceptionHandler {
@ExceptionHandler(value = {MethodArgumentNotValidException.class,BindException.class})
public ErrorResponse handleMethodArgumentNotValidException(HttpServletResponse response, MethodArgumentNotValidException ex) {
if (log.isErrorEnabled()) {
log.error(ex.getMessage(), ex);
}
BindingResult result = ex.getBindingResult();
FieldError error = result.getFieldError();
String message = error.getDefaultMessage();
response.setStatus(400);
return ErrorResponse.builder().code("ARGUMENT_VALIDATION_ERROR").message(message).build();
}
@ExceptionHandler(value = {ConstraintViolationException.class})
public ErrorResponse handleBindException(HttpServletResponse response, ConstraintViolationException ex) {
if (log.isErrorEnabled()) {
log.error(ex.getMessage(), ex);
}
response.setStatus(400);
return ErrorResponse.builder().code("BIND_VALIDATION_ERROR").message(ex.getMessage()).build();
}
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ErrorResponse {
private String code;
private String message;
}
这样一来,参数校验就会方便很多了。