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

Posted 码王信息

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用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 {};

  Class<? extends 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进行讨论和学习。

以上是关于使用validator-api来验证spring-boot的参数的主要内容,如果未能解决你的问题,请参考以下文章

我是不是需要使用 Google 帐户身份验证而不是 Spring 身份验证来避免 Firesheep cookie 嗅探攻击?

使用来自 Spring Security 的密码来验证 REST 调用

Spring Security 结合 Spring 存储库来保护和验证网站的各个部分

是否可以在spring中直接访问db表来验证状态

使用 OpenID Connect Gluu 身份验证提供程序来保护 Spring Boot Web App 客户端

如何使用 Spring Security 3.1.3 和 JSF 创建一个 Bean 来验证我的登录表单