springboot参数传递
- @PathVariable
- @RequestParam
- @RequestBody
JSR303
jsr303 : 也称 bean validation 规范,用于java bean 验证的标准API,,他定义了一组注解,可以在javabean 的属性上声明验证规则
JSR: java specification request
常用注解:
- @NotNull
- @Size : 定义属性的最小和最大长度
- @Min
- @Max
- @Pattern : 属性值必须匹配正则
- @Valid : 对嵌套对象或者集合触发验证
springboot中校验注解
在类上打上 @Validated 才会开启校验
校验javabean 也要设置 @Validated ,,javabean中的每一个字段,也需要验证,,字段可能是一个普通字段,也可能是对象,,如果是对象,,使用@Valid 对嵌套对象触发验证
@Valid
和 @Validated
区别: 这两个都可以对javabean 开启验证,,@Valid是java提供的,,@Validated 是spring 对@Valid的扩展,,一般开启验证使用 @Validated,指定嵌套验证使用@Valid
自定义校验注解
自定义一个注解,校验两次输入密码一样:
- 这个注解需要遵循一定的规范,他有两个模板方法,必须要有
@Documented // 注解里面的注释加入到文档
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Constraint(validatedBy = PasswordValidator.class) // 指定多个校验类
public @interface PasswordEquals {
int min() default 3;
int max() default 6;
String message() default "password not equal";
/**
* 如果要自定义校验 有两个模板方法,必须加上,,规范必须要有这两个东西
*/
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
- 写你自己的验证逻辑,实现
ConstraintValidator
接口,,这个接口第一个泛型是:你自定义的注解,, 第二个泛型是: 自定义注解要打在什么类型的数据上,,如果打在类上,就是类的类型,如果打在字段上,就是字段的类型
这里面可以获取注解传入的值,,和javabean传入的值,,在isValid
方法中进行比较,返回true,则验证通过
/**
* 泛型:
* <自定义注解的类型,自定义注解修饰的目标的类型>
*
*
* 如果注解在类上,,修饰的是类
* 如果注解在字段上,,修饰的是字段 ,,就是字段的类型
*/
public class PasswordValidator implements ConstraintValidator<PasswordEquals, PersonDTO> {
private int min;
private int max;
/**
* 校验通过 返回true,不通过返回false
*/
@Override
public boolean isValid(PersonDTO personDTO, ConstraintValidatorContext context) {
// 没有考虑密码为空
String password01 = personDTO.getPassword01();
String password02 = personDTO.getPassword02();
return password01.equals(password02);
}
/**
* 获取注解传入的值
*/
@Override
public void initialize(PasswordEquals constraintAnnotation) {
this.min = constraintAnnotation.min();
this.max = constraintAnnotation.max();
ConstraintValidator.super.initialize(constraintAnnotation);
}
}
- 将自定义的 ContraintValidator 绑定到指定注解上
一个注解可以绑定多个ConstraintValidator
指定多个校验类
根据不同的判断,设置不同的错误信息:
public class PasswordEqualValidator implements ConstraintValidator<PasswordEqual, Person> {
private Integer min;
private Integer max;
@Override
public void initialize(PasswordEqual constraintAnnotation) {
this.max = constraintAnnotation.max();
this.min = constraintAnnotation.min();
}
@Override
public boolean isValid(Person person, ConstraintValidatorContext context) {
String password01 = person.getPassword01();
String password02 = person.getPassword02();
if (password01.length() < min){
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("长度不能小于3").addConstraintViolation();
return false;
}
return password01.equals(password02);
}
}
参数校验的异常
- ConstraintViolationException
路径中传递参数报错 ,比如 @PathVarible ,,@RequestParam 传递的路径参数和query参数报错
这个异常: 系统会自动拼接这些参数的错误,,在异常的message中返回
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public UnifyResponse handleConstraintViolationException(HttpServletRequest request,ConstraintViolationException e){
System.out.println("e = " + e);
// 所有的验证
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
// 在 异常的 message 中拼接好了,,如果是在自定义异常要定制化显示,,就需要遍历 ConstraintViolation自己拼接
return new UnifyResponse(10001, e.getMessage(), request.getMethod() + " " + request.getRequestURI());
}
- MethodArgumentNotValidException
在对javabean验证的时候报错,,,多个校验器没有通过会返回一个集合,,需要自己拼接错误信息,所有异常都要处理,,不能只处理一个
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST) // 400 参数错误
public UnifyResponse handleBeanValidation(HttpServletRequest request,MethodArgumentNotValidException e){
String uri = request.getRequestURI();
String method = request.getMethod();
// 多个校验器都没有通过,,返回集合
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
Optional<String> errorMsg = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).reduce((a, b) -> (a + "," + b ));
System.out.println("errormsg = " + errorMsg);
return new UnifyResponse(10001, errorMsg.orElse(""), method + " " + uri);
}