跳到内容

Bean Validation 速查表

简介

本文旨在为在您的应用程序中提供 Java Bean Validation 安全功能,提供清晰、简单、可操作的指导。

Bean validation(又称 Jakarta Validation)是 Java 中执行输入验证最常见的方法之一。它是一个与应用层无关的验证规范,为开发人员提供了在领域模型上定义一组验证约束,然后通过各种应用层执行这些约束验证的手段。

这种方法的一个优点是,验证约束和相应的验证器只编写一次,从而减少了重复工作并确保了统一性

典型验证

Typical

Bean Validation

JSR

设置

本指南中的示例使用 Hibernate Validator。

将 Hibernate Validator 添加到您的 pom.xml

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>USE_LATEST_VERSION</version>
</dependency>

在 Spring 的 context.xml 中启用 Bean 验证支持

<beans:beans ...
   ...
   <mvc:annotation-driven />
   ...
</beans:beans>

更多信息,请参阅设置指南

基础知识

要开始使用 Bean Validation,您必须将验证约束(@Pattern, @Digits, @Min, @Max, @Size, @Past, @Future, @CreditCardNumber, @Email, @URL 等)添加到您的模型中,然后在不同的应用层传递模型时使用 @Valid 注解。

约束可以应用于多个地方

  • 字段
  • 属性

对于 Bean Validation 1.1 也适用于

  • 参数
  • 返回值
  • 构造函数

为简单起见,以下所有示例都以字段约束为特色,并且所有验证都由控制器触发。有关完整示例列表,请参阅 Bean Validation 文档。

在错误处理方面,Hibernate Validator 返回一个包含 List<ObjectError>BindingResult 对象。下面的示例展示了简单的错误处理,而生产就绪的应用程序将拥有更精巧的设计,负责日志记录和错误页面重定向。

预定义约束

@Pattern

注解:

@Pattern(regex=,flag=)

数据类型:

CharSequence

用途:

检查被注解的字符串是否与给定的标志匹配的正则表达式 regex 匹配。请访问 OWASP 验证正则表达式仓库以获取其他有用的正则表达式。

参考:

文档

模型:

import org.hibernate.validator.constraints.Pattern;

public class Article  {
 //Constraint: Alpha Numeric article titles only using a regular expression
 @Pattern(regexp = "[a-zA-Z0-9 ]")
 private String articleTitle;
 public String getArticleTitle()  {
  return  articleTitle;
 }
 public void setArticleTitle(String  articleTitle)  {
   this.articleTitle  =  articleTitle;
  }

  ...

}

控制器:

import javax.validation.Valid;
import com.company.app.model.Article;

@Controller
public class ArticleController  {

 ...

 @RequestMapping(value = "/postArticle",  method = RequestMethod.POST)
 public @ResponseBody String postArticle(@Valid  Article  article,  BindingResult  result,
 HttpServletResponse  response) {
  if (result.hasErrors()) {
   String errorMessage  =  "";
   response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
   List<ObjectError> errors = result.getAllErrors();
   for(ObjectError  e :  errors) {
    errorMessage += "ERROR: " +  e.getDefaultMessage();
   }
   return  errorMessage;
  } else {
   return  "Validation Successful";
  }
 }
}

@Digits

注解:

@Digits(integer=,fraction=)

数据类型:

BigDecimal, BigInteger, CharSequence, byte, short, int, long 以及对应的原始类型包装类;HV 额外支持:Number 的任何子类型

用途:

检查被注解的值是否是一个数字,最多包含 integer 位整数和 fraction 位小数

参考:

文档

模型:

import org.hibernate.validator.constraints.Digits;

public class Customer {
  //Constraint: Age can only be 3 digits long or less
  @Digits(integer = 3, fraction = 0)
  private int age;

  public String getAge()  {
    return age;
  }

  public void setAge(String age)  {
      this.age = age;
    }

    ...
}

控制器:

import javax.validation.Valid;
import com.company.app.model.Customer;

@Controller
public class CustomerController  {

 ...

 @RequestMapping(value = "/registerCustomer",  method = RequestMethod.POST)
 public @ResponseBody String registerCustomer(@Valid Customer customer, BindingResult result,
 HttpServletResponse  response) {

  if (result.hasErrors()) {
   String errorMessage = "";
   response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
   List<ObjectError> errors = result.getAllErrors();

   for( ObjectError  e :  errors) {
    errorMessage += "ERROR: "  +  e.getDefaultMessage();
   }
   return  errorMessage;
  } else {
   return  "Validation Successful";
  }
 }
}

@Size

注解:

@Size(min=, max=)

数据类型:

CharSequence, Collection, MapArrays

用途:

检查被注解元素的尺寸是否在 min 和 max 之间(包含边界)

参考:

文档

模型:

import org.hibernate.validator.constraints.Size;

public class Message {

   //Constraint: Message must be at least 10 characters long, but less than 500
   @Size(min = 10, max = 500)
   private String message;

   public String getMessage() {
      return message;
   }

   public void setMessage(String message) {
      this.message = message;
   }

...
}

控制器:

import javax.validation.Valid;
import com.company.app.model.Message;

@Controller
public class MessageController {

...

@RequestMapping(value="/sendMessage", method=RequestMethod.POST)
public @ResponseBody String sendMessage(@Valid Message message, BindingResult result,
HttpServletResponse response){

   if(result.hasErrors()){
      String errorMessage = "";
      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
      List<ObjectError> errors = result.getAllErrors();
      for( ObjectError e : errors){
         errorMessage+= "ERROR: " + e.getDefaultMessage();
      }
      return errorMessage;
   }
   else{
      return "Validation Successful";
   }
}
}

@Past / @Future

注解:

@Past, @Future

数据类型:

java.util.Date, java.util.Calendar, java.time.chrono.ChronoZonedDateTime, java.time.Instant, java.time.OffsetDateTime

用途:

检查被注解的日期是否在过去 / 将来

参考:

文档

模型:

import org.hibernate.validator.constraints.Past;
import org.hibernate.validator.constraints.Future;

public class DoctorVisit {

   //Constraint: Birthdate must be in the past
   @Past
   private Date birthDate;

   public Date getBirthDate() {
      return birthDate;
   }

   public void setBirthDate(Date birthDate) {
      this.birthDate = birthDate;
   }

   //Constraint: Schedule visit date must be in the future
   @Future
   private String scheduledVisitDate;

   public String getScheduledVisitDate() {
      return scheduledVisitDate;
   }

   public void setScheduledVisitDate(String scheduledVisitDate) {
      this.scheduledVisitDate = scheduledVisitDate;
   }

...
}

控制器:

import javax.validation.Valid;
import com.company.app.model.DoctorVisit;

@Controller
public class DoctorVisitController {

   ...

   @RequestMapping(value="/scheduleVisit", method=RequestMethod.POST)
   public @ResponseBody String scheduleVisit(@Valid DoctorVisit doctorvisit, BindingResult result,
   HttpServletResponse response){

      if(result.hasErrors()){
         String errorMessage = "";
         response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
         List<ObjectError> errors = result.getAllErrors();
         for( ObjectError e : errors){
            errorMessage+= "ERROR: " + e.getDefaultMessage();
         }
         return errorMessage;
      }
      else{
         return "Validation Successful";
      }
   }
}

组合约束

验证注解可以以任何合适的方式组合。例如,要指定一个介于 1 到 5 之间的有效 reviewRating 值,可以这样指定验证:

注解:

@Min(value=), @Max(value=)

数据类型:

BigDecimal, BigInteger, byte, short, int, long 以及对应的原始类型包装类;HV 额外支持:CharSequence 的任何子类型(评估字符序列表示的数值),Number 的任何子类型

用途:

检查被注解的值是否大于/小于或等于指定的最小值

参考

文档

模型:

import org.hibernate.validator.constraints.Min;
import org.hibernate.validator.constraints.Max;

public class Review {

 //Constraint: Review rating must be between 1 and 5
 @Min(1)
 @Max(5)
 private int reviewRating;

 public int getReviewRating() {
   return reviewRating;
 }

 public void setReviewRating(int reviewRating) {
   this.reviewRating = reviewRating;
}
 ...
}

控制器:

import javax.validation.Valid;
import com.company.app.model.ReviewRating;

@Controller
public class ReviewController {

   ...

   @RequestMapping(value="/postReview", method=RequestMethod.POST)
   public @ResponseBody String postReview(@Valid Review review, BindingResult result,
   HttpServletResponse response){

      if(result.hasErrors()){
         String errorMessage = "";
         response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
         List<ObjectError> errors = result.getAllErrors();
         for( ObjectError e : errors){
            errorMessage+= "ERROR: " + e.getDefaultMessage();
         }
         return errorMessage;
      }
       else{
         return "Validation Successful";
      }
   }
}

级联约束

验证一个 bean 是一个好的开始,但通常,bean 是嵌套的或处于一个完整的 bean 图中。要一次性验证该图,请使用 @Valid 应用级联验证

附加约束

除了提供完整的 JSR303 约束集外,Hibernate Validator 还定义了一些额外的约束以方便使用

  • @CreditCardNumber
  • @EAN
  • @Email
  • @Length
  • @Range
  • @ScriptAssert
  • @URL

请查看此列表以获取完整列表。

请注意,根据 Hibernate Validator 6.1.0.Final 和 6.0.18.Final 发布博客文章@SafeHtml(一个以前有效的约束)已被弃用。请避免使用 @SafeHtml 约束。

自定义约束

Bean 验证最强大的功能之一是能够定义自己的约束,这些约束超越了内置约束提供的简单验证。

创建自定义约束超出了本指南的范围。请参阅此文档

错误消息

可以使用验证注解指定消息 ID,从而自定义错误消息

@Pattern(regexp = "[a-zA-Z0-9 ]", message="article.title.error")
private String articleTitle;

Spring MVC 随后将在已定义的 MessageSource 中查找 ID 为 article.title.error 的消息。有关更多信息,请参阅此文档