Bean Validation 速查表¶
简介¶
本文旨在为在您的应用程序中提供 Java Bean Validation 安全功能,提供清晰、简单、可操作的指导。
Bean validation(又称 Jakarta Validation)是 Java 中执行输入验证最常见的方法之一。它是一个与应用层无关的验证规范,为开发人员提供了在领域模型上定义一组验证约束,然后通过各种应用层执行这些约束验证的手段。
这种方法的一个优点是,验证约束和相应的验证器只编写一次,从而减少了重复工作并确保了统一性
典型验证¶
Bean Validation¶
设置¶
本指南中的示例使用 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
, Map
和 Arrays
用途:
检查被注解元素的尺寸是否在 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 的消息。有关更多信息,请参阅此文档。