使用validator-api来验证spring-boot的参数

作为服务端开发,验证前端传入的参数的合法性是一个必不可少的步骤,但是验证参数是一个基本上是一个体力活,而且冗余代码繁多,也影响代码的可阅读性,所以有没有一个比较优雅的方式来解决这个问题?

这么简单的问题当然早就有大神遇到并且解决了,这一篇文章主要讲一下解决基于spring-boot的验证参数的比较好的方法:利用validator-api来进行验证参数。

spring-boot-starter-web包里面有hibernate-validator包,它提供了一系列验证各种参数的方法,所以说spring-boot已经帮我们想好要怎么解决这个问题了。

这篇文章针对spring-boot里面的spring-mvc介绍三种方式来验证参数。

(一):这个方法在网上大部分都可以查到,先假设我们的restful的接口接受一个GradeAndClassroomModel类型的对象,并且这个类被定义成

@Data
public class GradeAndClassroomModel {  
@Range(min = 1, max = 9, message = "年级只能从1-9")  
private int grade;  
@Range(min = 1, max = 99, message = "班级只能从1-99")  
private int classroomNumber;
}

利用validator提供的一系列注解,比如本例中的@Range,就可以表示参数的范围和出错时候的提示信息。还有很多其他注解,这里就不一一列出

然后我们的Controller层的代码为

@RequestMapping(value = "/paramErrorTest", method = RequestMethod.GET)
public String paramErrorTest(    
  @Valid    
  @ModelAttribute    
  GradeAndClassroomModel gradeAndClassroomModel, 
  BindingResult result) {  
  return classroomService.getTeacherName(gradeAndClassroomModel.getGrade(), gradeAndClassroomModel.getClassroomNumber());
}

其中如果验证出错,result对象里面就会有错误信息,然后可以自己进行处理。

(二): 针对上面的例子,会有人说,就两个参数,为什么要作为对象呢?会不会太麻烦?确实,如果只有少数对象,直接把参数写到Controller层,然后在Controller层进行验证就可以了。

@RequestMapping(value = "/teacherName", method = RequestMethod.GET)
public String teacherName(
  @Range(min = 1, max = 9, message = "年级只能从1-9")        
  @RequestParam(name = "grade", required = true) 
  int grade,  
  @Min(value = 1, message = "班级最小只能1")    
  @Max(value = 99, message = "班级最大只能99")      
  @RequestParam(name = "classroom", required = true)    
  int classroom) {  
return classroomService.getTeacherName(grade, classroom);
}

如果直接把validator提供的注解移除来写到请求参数上面的话是不是就可以了呢?答案是错,为什么这样不能成功的验证参数呢?具体原因大家可以参考官方文档:http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/htmlsingle/#validation-beanvalidation-spring-method

上面的文档已经说的很清楚了,所以我们需要创建一个Bean

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {  
  return new MethodValidationPostProcessor();
}

然后在类方法上面加上注解@Validated

@RestController
@RequestMapping("/spring-boot/classroom")
@Validated
public class ClassroomController {
 ...
}

然后之前没有生效的注解@Range@Min@Maxvalidator包里面提供的注解就可以生效了。

(三)估计到了这里又会有人问,如果validator包里面注解不能满足我们的需求,我们是否可以自己定义参数验证的逻辑。答案是肯定的,我们可以利用

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Constraint(validatedBy = {Validator.class})
public @interface ParamValidator {

  String message() default "Parameter error!";

  Class[] groups() default {};

  Classextends Payload>[] payload() default {};

}

public class Validator implements ConstraintValidator<ParamValidator, Object> {
  ...
}

组合进行自定义,具体的例子网上其他文章就很多了,这里就不进行详细的例子了,但是最终使用的时候就是

  @RequestMapping(value = "/paramValidator", method = RequestMethod.GET)
  public String paramValidator(
      @ParamValidator(isRequired = true, desc = "年级", range = "int:1~9", message = "年级只能从1-9")
      @RequestParam(name = "grade", required = true)
      int grade,
      @ParamValidator(isRequired = true, desc = "班级", range = "int:1~99", message = "班级只能从1-99")
      @RequestParam(name = "classroom", required = true)
      int classroom) {
    return classroomService.getTeacherName(grade, classroom);
  }

另外不要忘记方法二里面里面提到的MethodValidationPostProcessor这个bean,如果没有初始化这个bean,自定义的验证方法也不会执行。验证逻辑会失效。

是不是通过这样写注解的方式来验证进行请求的参数,代码逻辑更佳清晰和优雅?表达的含义也会更佳清楚?并且没有了大量重复的类似的验证代码。

Ps:这里的代码都是基于spring-mvc框架来试验的,如果有人并没有使用spring-mvc作为rest框架,而是使用jersey来作为rest框架的话,可能一些细节方面需要调整, 但是这三种方案应该都是可以兼容的。

最后如果觉得所讲的东西能够帮助到你,并且希望了解更多的知识,进行更详细的深入的学习,欢迎加群632109190进行讨论和学习。

你可能感兴趣的:(java)