1.springboot已经整合Validation
jar包坐标
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.定义相关的异常处理类(并配置快速校验失败返回结果,可选)
import lombok.extern.slf4j.Slf4j;
import loss.reduction.utils.Result;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.stream.Collectors;
/**
* 定义全局处理异常
*/
@RestControllerAdvice
@Slf4j
@Configuration
public class ExceptionHandlerConfig {
/**
* 全局处理异常
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e){
e.printStackTrace();
log.error(e.getMessage());
return new Result().error(e.getMessage());
}
/**
* 处理 form data方式调用接口校验失败抛出的异常
* @param e
* @return
*/
@ExceptionHandler(BindException.class)
public Result handlerException(BindException e){
BindingResult bindingResult = e.getBindingResult();
String result = bindingResult.getFieldErrors().stream().map(o -> o.getDefaultMessage()).collect(Collectors.joining(","));
return new Result().error(result);
}
/**
* 处理 json 请求体调用接口校验失败抛出的异常
* @param e
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handlerException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
String result = bindingResult.getFieldErrors().stream().map(o -> o.getDefaultMessage()).collect(Collectors.joining(","));
return new Result().error(result);
}
/**
* 处理requestParam/PathVariable参数校验的异常
* @param e
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
public Result handlerException(ConstraintViolationException e){
String result = e.getConstraintViolations().stream().map(o -> o.getMessage()).collect(Collectors.joining(","));
return new Result().error(result);
}
/**
* spring Validation默认校验完所有参数才会返回结果,可将failFast设置为true,一旦校验失败就会结果校验并返回结果
* @return
*/
@Bean
public Validator validator(){
ValidatorFactory factory=Validation.byProvider(HibernateValidator.class).
configure().failFast(true).buildValidatorFactory();
return factory.getValidator();
}
}
3.requestBody参数校验(json格式参数校验)
3.1.实体类
@ApiModel
@Data
public class TestValidationDto {
@NotNull(message = "id不能为空")
@ApiModelProperty("id")
private Integer id;
@NotBlank(message = "name不能为空")
@Length(max = 10,min = 2,message = "姓名长度2-10")
@ApiModelProperty("姓名")
private String name;
@Min(value = 1,message = "年龄不能小于1")
@Max(value = 100,message = "年龄不能大于100")
@ApiModelProperty("年龄")
@NotNull(message = "年龄不能为空")
private Integer age;
}
3.2.controller层
@RestController
@RequestMapping("/test")
public class TestController {
@ApiOperation("测试")
@PostMapping ("update")
public Result update(@RequestBody @Validated TestValidationDto dto) {
return new Result().ok("11");
}
}
这种情况下,使用@Valid和@Validated都可以。
4.requestParam/PathVariable参数校验
GET请求一般会使用requestParam/PathVariable传参。如果参数比较多 (比如超过 5 个),还是推荐使用DTO对象接收。否则,推荐将一个个参数平铺到方法入参中。在这种情况下,必须在Controller类上标注@Validated注解,并在入参上声明约束注解 (如@Min等)。如果校验失败,会抛出ConstraintViolationException异常。代码示例如下:
@RestController
@RequestMapping("/test")
//对get请求的参数生效
@Validated
@Slf4j
public class TestController {
@GetMapping("test1")
public Result test1(@RequestParam @NotBlank(message = "name不能为空") String name, @RequestParam @Max(value = 100,message = "id不能大于100") Integer id) {
log.info("name:{}",name);
return new Result().ok("1111");
}
@GetMapping("test2/{id}")
public Result test2(@PathVariable @Max(value = 100,message = "id不能大于100") Integer id) {
log.info("id:{}",id);
return new Result().ok("1111");
}
}
5.分组校验
5.1.实体类
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.groups.Default;
@ApiModel
@Data
public class TestValidationDto {
@NotNull(message = "id不能为空",groups ={Update.class} )
@ApiModelProperty("id")
private Integer id;
@NotBlank(message = "name不能为空",groups = {Insert.class,Update.class})
@Length(max = 10,min = 2,message = "姓名长度2-10")
@ApiModelProperty("姓名")
private String name;
@Min(value = 1,message = "年龄不能小于1")
@Max(value = 100,message = "年龄不能大于100")
@ApiModelProperty("年龄")
@NotNull(message = "年龄不能为空")
private Integer age;
/**
* 更新组
* 继承Default接口的好处:校验组内的属性的同时,也会校验未分组的属性
*/
public interface Update extends Default{
}
/**
* 新增组
* 继承Default接口的好处:校验组内的属性的同时,也会校验未分组的属性
*/
public interface Insert extends Default {
}
}
5.2.controller
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
@ApiOperation("测试更新")
@PostMapping ("update")
public Result update(@RequestBody @Validated(TestValidationDto.Update.class) TestValidationDto dto) {
return new Result().ok("11");
}
@ApiOperation("测试新增")
@PostMapping ("insert")
public Result insert(@RequestBody @Validated(TestValidationDto.Insert.class) TestValidationDto dto) {
return new Result().ok("11");
}
}
6.嵌套校验/递归校验
前面的示例中,DTO类里面的字段都是基本数据类型和String类型。但是实际场景中,有可能某个字段也是一个对象,这种情况先,可以使用嵌套校验。
比如,上面保存TestValidationDto信息的时候同时还带有Student信息。需要注意的是,此时DTO类的对应字段必须标记@Valid注解(对于集合同样适用)。
6.1.实体类
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.groups.Default;
@ApiModel
@Data
public class TestValidationDto {
@NotNull(message = "id不能为空",groups ={Update.class} )
@ApiModelProperty("id")
private Integer id;
@NotBlank(message = "name不能为空",groups = {Insert.class,Update.class})
@Length(max = 10,min = 2,message = "姓名长度2-10")
@ApiModelProperty("姓名")
private String name;
@Min(value = 1,message = "年龄不能小于1")
@Max(value = 100,message = "年龄不能大于100")
@ApiModelProperty("年龄")
@NotNull(message = "年龄不能为空")
private Integer age;
@ApiModelProperty("学生对象")
@Valid
private Student student;
/**
* 更新组
* 继承Default接口的好处:校验组内的属性的同时,也会校验未分组的属性
*/
public interface Update extends Default{
}
/**
* 新增组
* 继承Default接口的好处:校验组内的属性的同时,也会校验未分组的属性
*/
public interface Insert extends Default {
}
@Data
public static class Student{
@ApiModelProperty("性别")
@NotBlank(message = "性别不能为空")
private String sex;
}
}
controller层
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
@ApiOperation("测试更新")
@PostMapping ("update")
public Result update(@RequestBody @Validated(TestValidationDto.Update.class) TestValidationDto dto) {
return new Result().ok("11");
}
@ApiOperation("测试新增")
@PostMapping ("insert")
public Result insert(@RequestBody @Validated(TestValidationDto.Insert.class) TestValidationDto dto) {
return new Result().ok("11");
}
}
7.自定义校验
7.1.自定义注解
```java
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
//标明由哪个类执行校验逻辑
@Constraint(validatedBy = {MyConstraintValidator.class })
public @interface ISBLANK {
String message() default "格式不对";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
7.2.校验逻辑类
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MyConstraintValidator implements ConstraintValidator<ISBLANK,String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isBlank(value)) {
return true;
}
return false;
}
}
7.3.使用
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel
@Data
public class TestValidationDto {
@ISBLANK(message = "班级必须为空")
private String class1;
}