写点什么

👊 【Spring 技术特性】SpringMVC 集成 Java Bean Validation 实现参数检验功能(上)

发布于: 刚刚
👊 【Spring 技术特性】SpringMVC集成Java Bean Validation实现参数检验功能(上)

目前 Bean Validation(JSR-349)的新特性可以到官网查看,之前用的参数校验有很多,主要集中于:


  • 跨参数验证(比如密码和确认密码的验证)

  • 支持在消息中使用 EL 表达式

  • 方法参数/返回值验证

  • CDI 和依赖注入

  • 分组转换


最开始接触 Java Bean Validation 的时候是 Hibernate validator 5,而且 spring4 才开始使用的,接下来我们从以下几个方法讲解 Bean Validation,当然不一定是新特性:


  • 集成 Java Bean Validation 到 SpringMVC

  • 分组验证、分组顺序及级联验证

  • 消息中使用 EL 表达式

  • 方法参数/返回值验证

  • 自定义验证规则

  • 类级别验证器

  • 脚本验证器

  • cross-parameter,跨参数验证

  • 混合类级别验证器和跨参数验证器

  • 组合多个验证注解

  • 本地化


因为大多数时候验证都配合 web 框架使用,而且很多朋友都咨询过如分组/跨参数验证,所以本文介绍下这些,且是和 SpringMVC 框架集成的例子,其他使用方式(比如集成到 JPA 中)可以参考其官方文档:


  • 规范:http://beanvalidation.org/1.1/spec/

  • hibernate validator 文档:http://hibernate.org/validator/

添加 hibernate validator 依赖:

<dependency>    <groupId>org.hibernate</groupId>    <artifactId>hibernate-validator</artifactId>    <version>latest.version</version></dependency>
复制代码


如果想在消息中使用 EL 表达式,请确保 EL 表达式版本是 2.2 或以上,至少需要进行 Tomcat7 以上。


<dependency>      <groupId>javax.el</groupId>      <artifactId>javax.el-api</artifactId>      <version>2.2.4</version>      <scope>provided</scope>  </dependency>  
复制代码


请确保您使用的 Web 容器有相应版本的 el jar 包。

web 服务的展示

@Controllerpublic class HelloWorldController {    @RequestMapping("/validate/hello")    public String validate(@Valided @ModelAttribute("user") UserModel user, Errors errors) {        if(errors.hasErrors()) {            return "validate/error";        }        return "redirect:/success";    }}
复制代码

硬编码错误消息

直接在验证约束注解上指定错误消息,如下所示:


@NotNull(message = "用户名不能为空")@Length(min=5, max=20, message="用户名长度必须在5-20之间")@Pattern(regexp = "^[a-zA-Z_]\\w{4,19}$", message = "用户名必须以字母下划线开头,可由字母数字下划线组成")private String username;
复制代码


如上所示,错误消息使用硬编码指定,这种方式是不推荐使用的,因为在如下场景是不适用的:


  1. 在国际化场景下,需要对不同的国家显示不同的错误消息;

  2. 需要更换错误消息时是比较麻烦的,需要找到相应的类进行更换,并重新编译发布。

交接于资源文件

默认的错误消息文件是/org/hibernate/validator/ValidationMessages.properties,如下图所示:



public class User implements Serializable {    @NotNull(message = "{user.id.null}")    private Long id;    @NotEmpty(message = "{user.name.null}")    @Length(min = 5, max = 20, message = "{user.name.length.illegal}")    @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}")    private String name;    @NotNull(message = "{user.password.null}")    private String password;}
复制代码

错误信息内容

默认的错误消息键值如下图所示:



  • 消息键默认为:验证约束注解的全限定类名.message。

  • 在我们之前的测试文件中,错误消息键值是使用默认的,如何自定义错误消息文件和错误消息键值呢?

自定义的错误消息文件和错误消息键值

  • 自定义错误消息文件里的错误消息键值将覆盖默认的错误消息文件中的错误消息键值。

  • 自定义错误消息文件是具有国际化功能的。

定义错误消息文件

在类装载路径的根下创建 ValidationMessages.properties 文件,如在 src 目录下创建会自动复制到类装载路径的根下,并添加如下消息键值


自定义的错误消息文件和错误消息键值自定义的错误消息文件里的错误消息键值将覆盖默认的错误消息文件中的错误消息键值。我们自定义的错误消息文件是具有国际化功能的。


javax.validation.constraints.Pattern.message=用户名必须以字母或下划线开头,后边可以跟字母数字下划线,长度在 5-20 之间。


  • org.springframework.validation.beanvalidation.LocalValidatorFactoryBean

  • org.hibernate.validator.HibernateValidator


此时错误消息键值的查找会先到 classpath 下 ValidationMessages.properties 中找,找不到再到默认的错误消息文件中找。


使用 Spring 的 MessageSource Bean 进行消息键值的查找如果我们的环境是与 spring 集成,还是应该使用 Spring 提供的消息支持,具体配置如下


  • org.springframework.context.support.ReloadableResourceBundleMessageSource

  • basename:classpath:messages

  • fileEncodings:utf-8

  • cacheSeconds:120


在消息文件 src/messages.properties 中添加如下错误消息:


  • javax.validation.constraints.Pattern.message=用户名必须以字母或下划线开头,后边可以跟字母数字下划线,长度在 5-20 之间。


配置了 messageSource Bean 时,默认将为验证的对象自动生成如下错误消息键:


  • 验证错误注解简单类名.验证对象名.字段名

  • 验证错误注解简单类名.字段名

  • 验证错误注解简单类名.字段类型全限定类名

  • 验证错误注解简单类名


使用的优先级是:从高到低,即最前边的具有最高的优先级,而且以上所有默认的错误消息键优先级高于自定义的错误消息键。


public String pattern(@Valided @ModelAttribute("model") PatternModel model, Errors errors)
复制代码


将自动产生如下错误消息键:


  • Pattern.model.value=验证错误注解简单类名.验证对象名.字段名

  • Pattern.value=验证错误注解简单类名.字段名

  • Pattern.java.lang.String=验证错误注解简单类名.字段类型全限定类名

  • Pattern=验证错误注解简单类名

自定义错误消息键值

大部分场景下,以上两种方式无法满足我们的需求,因此我们需要自定义错误消息键值。


public class PatternModel {    @Pattern(regexp = "^[a-zA-Z_][\\w]{4,19}$", message="{user.name.error}")    private String value;}
复制代码


在消息文件 src/messages.properties 中添加如下错误消息:


user.name.error=用户名格式不合法


在消息文件 src/messages.properties 中添加如下错误消息:


@Length(min=5, max=20, message="{user.name.length.error}")  
复制代码


  • user.name.length.error=用户名长度必须在 5-20 之间


错误消息中的 5-20 应该是从 @Length 验证约束注解中获取的,而不是在错误消息中硬编码,因此我们需要占位符的支持:


@Length(min=5, max=20, message="{user.name.length.error}")
复制代码


错误消息可以这样写:用户名长度必须在{min}-{max}之间。


错误消息占位符规则:{验证注解属性名}:


  • @Length 有 min 和 max 属性,则在错误消息文件中可以通过{min}和{max}来获取;

  • @Max 有 value 属性,则在错误消息文件中可以通过{value}来获取。


user.name.length.error=用户名长度必须在{min}-{max}之间
复制代码


功能处理方法上多个验证参数的处理


当我们在一个功能处理方法上需要验证多个模型对象时,需要通过如下形式来获取验证结果:


@RequestMapping("/validate/multi")  public String multi(              @Valid @ModelAttribute("a") A a, BindingResult aErrors,              @Valid @ModelAttribute("b") B b, BindingResult bErrors) {          if(aErrors.hasErrors()) { //如果a模型对象验证失败              return "validate/error";          }          if(bErrors.hasErrors()) { //如果a模型对象验证失败              return "validate/error";          }          return "redirect:/success";  }
复制代码


每一个模型对象后边都需要跟一个 Errors 或 BindingResult 对象来保存验证结果,其方法体内部可以使用这两个验证结果对象来选择出错时跳转的页面。

发布于: 刚刚阅读数: 3
用户头像

🏆 2021年InfoQ写作平台-签约作者 🏆 2020.03.25 加入

【个人简介】酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“ 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域、Spring生态、MySQL专项、APM专题及微服务/分布式体系等

评论

发布
暂无评论
👊 【Spring 技术特性】SpringMVC集成Java Bean Validation实现参数检验功能(上)