前言
@Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束。
这个技术一直都想进行梳理了很多复杂校验不会使用,本章节用来进行梳理
validated 用于参数的校验,支持简单和复杂(分组/嵌套)校验
@Validated / @Valid 的区别
功能@validated 提供了分组能力而@Valid没有
位置@Valid作用于 方法\构造方法\方法参数\成员属性@Validdated作用于 类\方法\方法参数
注意事项
可能不生效的几个原因:1. @validate 默认将所有校验加入到了Default组中,如果指定了自定义组将不会校验Default组中的校验,如果需要校验默认组需要加上Default组,面对复杂的校验很有可能会搞混2. 如果需要使用分组,那么定义分组类型需要是接口类型,否则会报错~~3. 如果使用了正则校验查看 正则是否正确
注解
逻辑图
Validatad 工作机制描述
调用者发送请求后端 -> Validatad拦截器将请求体进行拦截后校验 -> 校验成功 -> 通过-> 校验失败 -> 抛出异常 -> 对异常进行处理(通过@RestControllerAdvice + @ExceptionHandler 注解进行异常处理)
依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
校验
目前有关于商品的接口需要进行校验
异常的处理
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;@RestControllerAdvice
public class MyControllerAdvice {@ExceptionHandler(MethodArgumentNotValidException.class)public String exceptionHandler(MethodArgumentNotValidException exception) {BindingResult result = exception.getBindingResult();StringBuilder stringBuilder = new StringBuilder();if (result.hasErrors()) {List<ObjectError> errors = result.getAllErrors();if (errors != null) {errors.forEach(p -> {FieldError fieldError = (FieldError) p;stringBuilder.append(fieldError.getDefaultMessage());});}}return stringBuilder.toString();}
}
简单校验
目前商品添加了一个新增接口需要进行校验
校验内容:商品的名称不能为空
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;@Data
@Accessors(chain = true)
public class MyTestDTO {@NotEmpty(message = "产品名称不能为空")private String product;
}
@RestController
@RequestMapping("api")
public class MyController {@PostMapping("addProduct")public MyTestDTO addProduct(@RequestBody @Validated MyTestDTO myTestDTO) {return myTestDTO;}
}
分组校验
分组: 如果在Controller中设置了分组,那么校验的时候只会走对应的分组拦截器,默认没有分组的校验都在javax.validation.groups.Default.class组中目前关于商品又增加了一个删除的接口原限制不变新增接口:产品名称不能为空修改接口:产品名称长度不能超过10位
//定义分组接口
public interface Group1 { //新增商品的分组
}
public interface Group2 { //修改商品的分组
}
import com.chen.advice.Group1;
import com.chen.advice.Group2;
import com.chen.pojo.MyTestDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("api")
public class MyController {@PostMapping("addProduct")public MyTestDTO addProduct(@RequestBody @Validated(Group1.class) MyTestDTO myTestDTO) {return myTestDTO;}@PutMapping("putProduct")public MyTestDTO putProduct(@RequestBody @Validated(Group2.class) MyTestDTO myTestDTO) {return myTestDTO;}}
import com.chen.advice.Group1;
import com.chen.advice.Group2;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;@Data
@Accessors(chain = true)
public class MyTestDTO {@NotEmpty(message = "产品名称不能为空",groups = Group1.class)@Size(max = 10,message = "产品名称长度不能超过10位",groups = Group2.class)private String product;
}
嵌套校验
注意事项:嵌套校验: 对象中的对象属性需要加上@valid的注解
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;@Data
@Accessors(chain = true)
public class MyTestDTO {@Size(max = 10,message = "产品名称长度不能超过10位")private String product;@Valid@NotNull(message = "医疗实体类不能为null")private YiLiaoDTO yiLiaoDTO;
}
import lombok.Data;
import javax.validation.constraints.Positive;@Data
public class YiLiaoDTO {@Positive(message = "产品数量必须为正整数")private Integer count;}
import com.chen.pojo.MyTestDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("api")
public class MyController {@PostMapping("addProduct")public MyTestDTO addProduct(@RequestBody @Validated MyTestDTO myTestDTO) {return myTestDTO;}
}
注解的尝试
自定义注解校验
注解类 -- 模仿自带的注解类进行编写
@Constraint(validateBy = 指定配置类)
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyValidCheck.class)
public @interface MyValid {int min() default 0;String message() default "{javax.validation.constraints.Size.message}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
配置类
initalize() 初始化方法用于获取注解设置的值
isValid() 判断是否通过校验
import com.example.demo.annotation.MyValid;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;public class MyValidCheck implements ConstraintValidator<MyValid,Object> {int min ;@Overridepublic void initialize(MyValid constraintAnnotation) {min = constraintAnnotation.min();}@Overridepublic boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {if (o != null && o.toString().length() <= min) {return true;}return false;}
}
Controller类
@RestController
@RequestMapping("api")
public class MyController {@PostMapping("addProduct")public MyTestDTO addProduct(@RequestBody @Validated MyTestDTO myTestDTO) {return myTestDTO;}
}
import com.example.demo.annotation.MyValid;
import lombok.Data;@Data
public class MyTestDTO {private String id;private String product;@MyValid(min = 10,message = "你小子不按规则走呀,输入10位以内")private Integer size;
}
正则校验
@Pattern(regexp = "^[A-Za-z0-9]+$",message = "不符合正则规定了哦小伙子")
private String name;
特殊校验
校验List<对象>
// 自行封装一个 List 没有经过尝试
@***Mapping("/**")
public *** apiName(@RequestBody @Validated(Add.class) ValidList<Bean> aObject)
package com.analog.greatbolderserver.config.valid;import lombok.Data;
import lombok.NoArgsConstructor;import javax.validation.Valid;
import java.util.*;/*** @ClassName ValidList* @Description ValidList* @Author TY* @Date 2020/8/26 16:05* @Version 1.0**/
@Data
@NoArgsConstructor
public class ValidList<E> implements List<E> {@Validprivate List<E> list = new LinkedList<>();public ValidList(List<E> paramList) {this.list = paramList;}@Overridepublic int size() {return list.size();}@Overridepublic boolean isEmpty() {return list.isEmpty();}@Overridepublic boolean contains(Object o) {return list.contains(0);}@Overridepublic Iterator<E> iterator() {return list.iterator();}@Overridepublic Object[] toArray() {return list.toArray();}@Overridepublic <T> T[] toArray(T[] a) {return list.toArray(a);}@Overridepublic boolean add(E e) {return list.add(e);}@Overridepublic boolean remove(Object o) {return list.remove(o);}@Overridepublic boolean containsAll(Collection<?> c) {return list.containsAll(c);}@Overridepublic boolean addAll(Collection<? extends E> c) {return list.addAll(c);}@Overridepublic boolean addAll(int index, Collection<? extends E> c) {return list.addAll(index, c);}@Overridepublic boolean removeAll(Collection<?> c) {return list.removeAll(c);}@Overridepublic boolean retainAll(Collection<?> c) {return list.retainAll(c);}@Overridepublic void clear() {list.clear();}@Overridepublic E get(int index) {return list.get(index);}@Overridepublic E set(int index, E element) {return list.set(index, element);}@Overridepublic void add(int index, E element) {list.add(index, element);}@Overridepublic E remove(int index) {return list.remove(index);}@Overridepublic int indexOf(Object o) {return list.indexOf(o);}@Overridepublic int lastIndexOf(Object o) {return list.lastIndexOf(o);}@Overridepublic ListIterator<E> listIterator() {return list.listIterator();}@Overridepublic ListIterator<E> listIterator(int index) {return list.listIterator(index);}@Overridepublic List<E> subList(int fromIndex, int toIndex) {return list.subList(fromIndex, toIndex);}
}