目录
- 1、引入依赖
- 2、原生校验注解说明
- 3、@validated和@valid不同点
- 4、使用Demo
- 4.1 单个参数校验
- 4.2 使用实体类校验
- 4.2.1 Controller层校验
- 4.2.1 Controller层调用service时在service层校验
- 4.2.2 serviceA调用serviceB时在serviceB层校验
- 4.4 参数校验异常全局捕获
- 5、自定义脚本检查@ScriptAssert
- 6、自定义校验注解,限定参数只能取某几个值中的一个的校验注解
- 7、嵌套校验
- 8、Demo源码
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2、原生校验注解说明
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) 验证字符串的长度
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
数值检查
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值
@DecimalMin 被标注的值必须不小于约束中指定的最小值
@Digits 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range 被注释的元素必须在合适的范围内
@ScriptAssert 自定义脚本检查
3、@validated和@valid不同点
- @valid 可以注解在成员属性(字段)上,但是@Validated只能用在类,方法和方法参数上面
- @valid 可以做嵌套校验
- 在controller层使用@valid不需要加@Validated。
- @Validated可以用在其他被spring管理的类上,从而让方法参数上面的@Size等注解生效
4、使用Demo
4.1 单个参数校验
注:需要使用@Validated开启校验
@RequestMapping("/getAge")
public String getAge(@Range(min = 18,max = 100) int age) {
return "限制年龄在18到100之间,age="+age;
}
4.2 使用实体类校验
实体类User:
@Data
@Accessors(chain = true)
public class User implements Serializable {
private static final long serialVersionUID = -5377638533206539277L;
private Long userId;
private String name;
@NotNull
@InValues(values = {"男","女"},canNull = false)
private String sex;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
private String address;
}
4.2.1 Controller层校验
@RequestMapping("/getUser")
public User getUser(@Valid User user) {
return user;
}
4.2.1 Controller层调用service时在service层校验
@RequestMapping("/saveUser1")
public boolean saveUser1(User user) {
return validServiceA.saveUserA(user);
}
@Validated
public interface ValidServiceA {
boolean saveUserA(@Valid User user);
}
4.2.2 serviceA调用serviceB时在serviceB层校验
@RequestMapping("/saveUser2")
public boolean saveUser2(User user) {
return validServiceA.saveUserAToB(user);
}
public interface ValidServiceA {
boolean saveUserAToB(User user);
}
@Validated
public interface ValidServiceB {
boolean saveUserB(@Valid User user);
}
4.4 参数校验异常全局捕获
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
public Map<String, Object> bindExceptionHandler(BindException e, HttpServletRequest request) {
log.error("GlobalExceptionHandler bindExceptionHandler exception={}", e.getMessage(), e);
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
List<String> msgList = new ArrayList<>();
for (ObjectError allError : allErrors) {
if(allError instanceof FieldError){
FieldError fieldError = (FieldError)allError;
msgList.add(fieldError.getObjectName() + "' on field '" + fieldError.getField() + "': rejected value [" + ObjectUtils.nullSafeToString(fieldError.getRejectedValue()) + "], message:"+allError.getDefaultMessage());
}else {
msgList.add(allError.toString());
}
}
Map<String, Object> result = new HashMap<String, Object>();
result.put("errorCode", "-999");
result.put("exceptionClass", e.getClass());
result.put("errorMsg", msgList.toString());
return result;
}
@ExceptionHandler(value = ConstraintViolationException.class)
public Map<String, Object> ConstraintViolationExceptionHandler(ConstraintViolationException e, HttpServletRequest request) {
log.error("GlobalExceptionHandler ConstraintViolationExceptionHandler exception={}", e.getMessage(), e);
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
List<String> msgList = new ArrayList<>();
while (iterator.hasNext()) {
ConstraintViolation<?> cvl = iterator.next();
msgList.add(cvl.getMessage());
}
Map<String, Object> result = new HashMap<String, Object>();
result.put("errorCode", "-999");
result.put("exceptionClass", e.getClass());
result.put("errorMsg", msgList.toString());
return result;
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Map<String, Object> handleDefaultException(Exception e) {
log.error("GlobalExceptionHandler handleDefaultException exception={}", e.getMessage(), e);
Map<String, Object> result = new HashMap<String, Object>();
result.put("errorCode", "-999");
result.put("exceptionClass", e.getClass());
result.put("errorMsg", e.getMessage());
return result;
}
}
5、自定义脚本检查@ScriptAssert
使用例子:
@Data
@Accessors(chain = true)
@ScriptAssert(script = "com.study.entity.User.checkName(_this.name)", lang = "javascript", message = "{script},用户名不能为空且不能包含! @ # $ % & * \\ / ? ?特殊符号")
public class User implements Serializable {
private static final long serialVersionUID = -5377638533206539277L;
private Long userId;
private String name;
@NotNull
@InValues(values = {"男","女"},canNull = false)
private String sex;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
private String address;
/**
* 检查用户名是否包含特殊字符
* @param name
* @return
*/
public static boolean checkName(String name) {
if (Objects.isNull(name)) {
return Boolean.FALSE;
}
if (name.length() > 4) {
return Boolean.FALSE;
}
String[] chars = {"!", "@", "#", "$", "%", "&", "*", "\\", "/", "?", "?"};
for (String c : chars) {
if (name.contains(c)) {
return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
}
@ScriptAssert
注解主要参数说明:
- lang
lang=“javascript” 表示用java 执行script中的脚本 - script
script指定特定的静态方法的全限定名称或者脚本(例如_this.password.equals(_this.rePassword)
),脚本必须返回Boolean.TRUE ,否则返回Boolean.FALSE - alias default “_this”
对象别名,在脚本中通过_this指代当前对象。
6、自定义校验注解,限定参数只能取某几个值中的一个的校验注解
@NotNull
@InValues(values = {"男","女"},canNull = false)
private String sex;
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {InValues.InValuesValidator.class})
public @interface InValues {
String message() default "value must be in {values}";
String[] values();
// 是否可以为空,默认为空,为空值则检查通过
boolean canNull() default true;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 指定多个时使用
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Documented
@interface List {
InValues[] value();
}
// 自定义检查逻辑
class InValuesValidator implements ConstraintValidator<InValues, Object> {
private String[] values;
private boolean canNull;
@Override
public void initialize(InValues constraintAnnotation) {
values = constraintAnnotation.values();
canNull = constraintAnnotation.canNull();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (Objects.nonNull(value)) {
for (String item : values) {
if (StringUtils.equals(item, String.valueOf(value))) {
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
return canNull;
}
}
}
7、嵌套校验
嵌套校验需要是使用@Valid注解
8、Demo源码