目录
  • @Valid和@Validated
    • @Valid和@Validated比较
    • @Valid高级使用
      • @Valid级联校验
    • @Validated高级使用
      • @Validated分组校验
      • @Validated分组校验顺序
      • @Validated非实体类校验
  • @PathVariable
    • 正则表达式校验
      • 继承BasicErrorController类
      • 自定义校验注解

        @Valid和@Validated

        @Valid和@Validated比较

        • 相同点:
          • @Valid注解和 @Validated注解都是开启校验功能的注解
        • 不同点:
          • @Validated注解是Spring基于 @Valid注解的进一步封装,并提供比如分组,分组顺序的高级功能
          • 使用位置不同:
            • @Valid注解 : 可以使用在方法,构造函数,方法参数和成员属性上
            • @Validated注解 : 可以用在类型,方法和方法参数上. 但是不能用在成员属性上

        @Valid高级使用

        @Valid级联校验

        • 级联校验: 也叫嵌套检测.嵌套就是一个实体类包含另一个实体类
        • @Valid和可以用在成员属性的字段上,因此 @Valid可以提供级联校验
        • 示例:
          @Data
          public class Hair {
          	
          	@NotBlank(message = "头发长度必须提交!")
          	private Double length;
          
            	@NotBlank(message = "头发颜色必须提交!")
            	private String color;
          }
          
          @Data
          public class Person {
          	
          	@NotBlank(message = "用户姓名必须提交!")
          	@Size(min=2, max=8)
          	private String userName;
          
            	// 添加@Valid注解实现嵌套检测
            	@Valid
              @NotEmpty(message = "用户要有头发!")
              private List<Hair> hairs;
          }
           
          @PostMapping("/person")
          public Result addPerson(@Valid @RequestBody Person person) {
          	return Result.buildSuccess(person);
          }
          • 只是在方法参数前面添加 @Valid@Validated注解,不会对嵌套的实体类进行校验.要想实现对嵌套的实体类进行校验,需要在嵌套的实体类属性上添加 @Valid注解

        @Validated高级使用

        @Validated分组校验

        • 分组校验:
          • 对指定的组开启校验,可以分别作用于不同的业务场景中
          • 分组校验是由 @Validated注解中的value提供的
        • groups:
          • JSR 303校验注解中的分组方法groups
          • 示例:
          @Data
          public class PersonGroup {
          	
          	public interface AddGroup {}
            
            	public interface UpdateGroup {}
          
            	// @Validated注解value方法指定分组UpdateGroup.class时校验
            	@NotBlank(message = "用户ID必须提交!", groups = UpdateGroup.class)
            	private String id;
          
            	// @Validated注解value方法指定分组AddGroup.class或者分组UpdateGroup.class时校验
            	@NotBlank(message = "用户的姓名必须提交!", groups = {AddGroup.class, UpdateGroup.class}) 
            	private String name;
          
            	// @Validated注解value方法未指定分组时校验
            	@Range(min = 1, max = 200, message = "用户的年龄必须提交!")
            	private int age;
          }
        • 开启分组校验: 通过 @Validated注解的value方法对指定的分组开启校验
        @RestController
        @RequestMapping("/person")
        public class PersonGroupController {
        	
        	// 不指定分组时校验
        	@GetMapping("/person")
        	public Result getPerson(@Validated @RequestBody PersonGroup person) {
        		return Result.buildSuccess(person);
        	}
        
        	// 指定AddGroup分组校验
        	@PostMapping("/person")
        	public Result addPerson(@Validated(value = PersonGroup.AddGroup.class) @RequestBody PersonGroup person) {
        		return Result.buildSuccess(person);
        	}
        
        	// 指定UpdateGroup分组校验
        	@PutMapping("/person")
        	public Result updatePerson(@Validated(value = PersonGroup.updateGroup.class) @RequestBody PersonGroup person) {
        		return Result.buildSuccess(person);
        	}
        }
        • 校验方法添加groups的值来指定分组,只有使用 @Validated注解的value的值指定这个分组时,开会开启注解的校验数据的功能

        @Validated分组校验顺序

        • 默认情况下,分组间的约束是无序的,但是在一些特殊的情况下可能对分组间的校验有一定的顺序
          • 比如第二组的分组的约束的校验需要依赖第一组的稳定状态来进行,此时,要求分组间的约束校验一定要有顺序
        • 分组校验顺序通过使用 @GroupSequence注解实现
        • 示例:
        @Data
        public class UserGroupSequence {
        	
        	public interface FirstGroup {}
        
        	public interface SecondGroup {}
        
        	// 使用GroupSequence定义分组校验顺序:按照FirstGroup,SecondGroup分组顺序进行校验
        	@GroupSequence({FirstGroup.class, SecondGroup.class})
        	public interface Group {}
        
        	@NotEmpty(message = "用户ID必须提交!", group = FirstGroup.class)
        	private String userId;
        
        	@NotEmpty(message = "用户姓名必须提交!", group = FirstGroup.class)
        	@Size(min = 2, max = 8, message = "用户姓名的长度在2~8之间", goup = Second.class)
        	private String userName;
        } 
        @RestController
        @RequestMapping("/user")
        public class UserGroupSequenceController {
        	// 这里方法中@Validated注解value的值是Group.class
        	@PostMapping("/user")
        	public Result addGroup(@Validated(value = Group.class) @RequestBody UserGroupSequence user) {
        		return Result.buildSuccess(user);
        	}
        }
        • 使用 @GroupSequence注解指定分组校验顺序后,第一组分组的约束的校验没有通过后,就不会进行第二组分组的约束的校验

        @Validated非实体类校验

        • 在非实体类上添加 @Validated注解对非实体类进行校验
        @Validated
        public class AnnotationController {
        	
        	@GetMapping("/person")
        	public Result getAge(@Range(min = 2, max = 8, message = "年龄在3~8岁!") @RequestParam int age) {
        		return Result.buildSuccess(age);
        	}
        }
        • GlobalExceptionHandler中添加全局统一异常处理方法:
        @ExceptionHandler(ConstraintViolationException.class)
        @ResponseBody
        public Result resolveConstraintViolationException(ConstraintVilationException exception) {
        	Set<ConstraintVilation<?>> constraintVilations = exception.getConstraintVilations();
        	// 处理异常信息
        	if (!CollectionUtils.isEmpty(constraintVilations)) {
        		StringBuilder messageBuilder = new StringBuilder();
        		for (ConstraintVilation constraintViolation : constraintVilations) {
        			messageBuilder.append(constraintVilation.getMessage()).append(",");
        		}
        		String errorMessage = messageBuilder.toString();
        		if (errorMessage.length() > 1) {
        			errorMessage.substring(0, errorMessage.length() - 1);
        		}
        		return Result.builderFailure(ErrorStatus.ILLEGAL_DATA.getCode(), errorMessage);
        	} 
        	return Result.builderFailure(ErrorStatus.ILLEGAL_DATA.getCode(), exception.getMessage())
        }

        @PathVariable

        • @PathVariable的作用: 用来指定请求URL路径里面的变量
        • @PathVariable@RequestParam的区别:
          • @PathVariable用来指定请求URL中的变量
          • @RequestParam用来获取静态的URL请求入参

        正则表达式校验

        • 使用正则表达式校验 @PathVariable指定的路径变量
        // 请求路径中的id必须是数字,否则寻找不到这个路径404
        @GetMapping("/user/{id:\\d+}")
        public Result getId(@PathVariable(name="id") String userId) {
        	return Result.buildSuccess(userId);
        }

        继承BasicErrorController类

        • @ControllerAdvice注解只能处理进入控制器方法抛出的异常
        • BasicErrorController接口可以处理全局异常
        • @PathVariable路径校验异常不是控制器方法抛出的,此时还没有进入控制器方法:
          • BasicErrorController处理异常,比如404异常时,会跳转到 /error路径,此时会返回错误的html页面
          • 为了保证返回结果统一,继承BasicErrorController类,重写BasicErrorController接口中的错误处理方法
        @RestController
        public class PathErrorController extends BasicErrorController {
        	
        	@Autowired
        	public PathErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List<ErrorViewResolver> errorViewResolvers) {
        		super(errorAttributes, serverProperties.getError(), errorViewResolvers);
        	}
        
        	/**
        	 * 处理html请求
        	 */
        	@Override
        	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        		HttpStatus status = getStatus(request);
        		Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        		ModelAndView modelAndView = new ModelAndView("pathErrorPage", model, status);
        		return modelAndView;
        	}
        	
        	/**
        	 * 处理json请求
        	 */
        	@Override
        	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        		Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        		
        		Map<String, Object> responseBody = new HashMap<>(8);
        		responseBody.put("success", false);
        		responseBody.put("code", body.get("status"));
        		responseBody.put("message", body.get("error")); 
        		
        		return new ResponseEntity<>(responseBody, HttpStatus.OK);
        	}
        } 

        自定义校验注解

        • 使用场景:
          • 对某一个只能输入指定值的字段进行校验. 此时需要使用自定义注解实现
        • 定义自定义的注解 @Show :
        @Documented
        @Constraint(validateBy = {Show.ShowConstraintValidator.class})
        @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
        @Rentation(RUNTIME)
        public @interface Show {
        	String message() default "{com.oxford.annotation.Show.message}";
        
        	Class<?>[] groups() default {};
        
        	Class<? extends Payload>[] payload() default {};
        	
        	int[] value();
        
        	class ShowConstraintValidator implements ConstraintValidator<Show, Integer> {
        		
        		private Set<Integer> set = new HashSet<>();
        
        		/**
        		 * 初始化操作
        		 * 获取value属性指定的数字,保存到Set集合中
        		 */
        		@Override
        		public void initilize(Show constraintAnnotation) {
        			int[] value = constraintAnnotation.value();
        			for (int v : value) {
        				set.add(i);
        			}
        		}	
        
        		@Override
        		public boolean isValid(Integer value, ConstraintValidatorContext context) {
        			return set.contains(value);
        		}
        	} 
        }
        • 注意点:
          • @Constraint注解:
            • 将自定义的注解和实现的校验类联系起来
          • 自定义校验注解类需要实现ConstraintValidator<A extends Annotation, T> 接口
            • 接口中第一个泛型参数表示的是自定义注解类
            • 接口中第二个泛型参数表示的是校验的属性的值的类型
          • initialize() 方法:
            • 获取到自定义注解中的相关的数据
          • isValid() 方法:
            • 实现自定义的校验逻辑
            • 返回boolean类型的校验结果
        • 自定义注解的使用:
        @Data
        public class AnnotationQuery {
        	
        	@Show(value = {0, 1}, message = "数值只能是0或者1")
        	private Integer isShow;
        }
        @PostMapping("/annotation")
        public Result addAnnotation(@Validated @RequestBody AnnotationQuery annotation) {
        	return Result.buildSuccess(annotation);
        }
        声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。