在这篇文章中,我们将学习如何使用 Spring MVC 自定义验证器。我们将探讨如何创建和使用您自己的自定义验证器。
Spring MVC 为 JSR-303 Bean Validation API 提供第一手支持。我们可以使用这个 API 来验证我们的输入数据。 Bean Validation API 附带了许多不同的验证器,可以通过使用简单的注释来使用,但是如果我们需要根据自定义业务规则验证数据,我们可以选择创建我们自己的自定义验证器并将其挂钩使用 *Spring MVC 验证框架 *。
我们将探索 Spring MVC Custom Validator 并了解如何将其挂接到我们的应用程序中。
为了创建和使用自定义验证器,我们需要在我们的类路径中验证 API 和 JSR-303 合规性验证 API,我们将在我们的示例中使用 Hibernate 验证器。首先,我们需要在 pom.xml
文件中添加以下条目
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
我们将在这篇文章中使用 Spring Boot,因此我们将使用 Spring Boot starters 在我们的类路径中添加验证 API。要在 Spring Boot 应用程序中使用 Hibernate 验证 API,您需要在 pom.xml
文件中添加以下启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
我们知道,使用 Spring Boot 时不需要指定版本,因为它会由 Spring Boot 自动处理。
为了理解它,让我们举一个简单的例子,我们要创建一个自定义验证器来验证输入表单中的两个字段,以确保它们相等。 (假设我们要确认客户的电子邮件地址)
我们将首先创建一个新的 @interface
来定义我们新的自定义约束。
@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface FieldMatch {
String message() default "Fields are not matching";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* @return The first field
*/
String first();
/**
* @return The second field
*/
String second();
/**
* Defines several <code>@FieldMatch</code> annotations on the same element
*
* @see FieldMatch
*/
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
FieldMatch[] value();
}
}
使用 @Constraint
注释,我们指定哪个类将实际执行验证,在我们的例子中,FieldMatchValidator
类将进行实际验证,message()
将定义将要执行的消息当验证失败时向客户显示,这是默认消息,可以通过配置进行更改(我们很快就会看到)。
让我们看看实际的验证类,它将负责执行我们的自定义验证。
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
private String firstFieldName;
private String secondFieldName;
public void initialize(final FieldMatch constraintAnnotation) {
this.firstFieldName = constraintAnnotation.first();
this.secondFieldName = constraintAnnotation.second();
}
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
try {
final Object firstObj = PropertyUtils.getProperty(value, this.firstFieldName);
final Object secondObj = PropertyUtils.getProperty(value, this.secondFieldName);
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
} catch (final Exception ex) {
//LOG.info("Error while getting values from object", ex);
return false;
}
}
}
我们的 Validation 类实现了 ConstraintValidator
接口并且应该实现 isValid
方法,我们所有的验证逻辑都将在这个方法中。在我们的自定义验证中,我们从两个字段中获取值并匹配它们以返回一个布尔值。
是时候看看我们的自定义验证在行动了,为了演示它,我们将使用一个简单的基于 Spring Boot 的 Web 应用程序,我们将验证 2 个字段,并在两个字段彼此不匹配的情况下抛出错误。
@Controller
public class CustomValidatorController {
@RequestMapping("/custom-validator")
public String hello(Person person) {
return "customValidator";
}
@PostMapping("/custom-validator")
public String demoValidation(@Valid Person person, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "customValidator";
}
return "redirect:/results";
}
@RequestMapping("/success")
public String welcome() {
return "welcome";
}
}
@FieldMatch(first = "email", second = "confirmEmail", message = "Email does not match")
public class Person {
@NotNull
private String name;
private String email;
private String confirmEmail;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getConfirmEmail() {
return confirmEmail;
}
public void setConfirmEmail(String confirmEmail) {
this.confirmEmail = confirmEmail;
}
}
如您所见,我们在 Person 类的顶部添加了 @FieldMatch
注释,它将比较 email
和 confirmEmail
字段,如果它们不匹配,它将抛出验证错误。
<html>
<body>
<form action="#" th:action="@{/custom-validator}" th:object="${person}" method="post">
<table>
<p th:if="${#fields.hasErrors('global')}" th:errors="*{global}">
Incorrect date
</p>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
</tr>
<tr>
<td>Email:</td>
<td><input type="text" th:field="*{email}" /></td>
<td th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</td>
</tr>
<tr>
<td>Confirm Email:</td>
<td><input type="text" th:field="*{confirmEmail}" /></td>
<td th:if="${#fields.hasErrors('confirmEmail')}" th:errors="*{confirmEmail}">Email Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
如果我们运行我们的应用程序并添加不匹配的电子邮件 ID,我们将在前端收到“电子邮件不匹配”错误。
我们还可以在字段级别定义自定义验证。我们需要使用相同的方法来定义字段级注释。
我们还可以在字段级别定义自定义验证。我们需要使用类似的方法来定义字段级别的注释。
@Constraint(validatedBy = CountryMatchValidator.class)
@Documented
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RUNTIME)
public @interface CountryMatch {
String message() default "Invalid Country";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Defines several <code>@FieldMatch</code> annotations on the same element
*
* @see FieldMatch
*/
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
FieldMatch[] value();
}
}
public class CountryMatchValidator implements ConstraintValidator<CountryMatch, String> {
/**
* Initializes the validator in preparation for
* {@link #isValid(Object, ConstraintValidatorContext)} calls.
* The constraint annotation for a given constraint declaration
* is passed.
* <p/>
* This method is guaranteed to be called before any use of this instance for
* validation.
*
* @param constraintAnnotation annotation instance for a given constraint declaration
*/
@Override
public void initialize(CountryMatch constraintAnnotation) {
}
/**
* Implements the validation logic.
* The state of {@code value} must not be altered.
* <p/>
* This method can be accessed concurrently, thread-safety must be ensured
* by the implementation.
*
* @param country country
* @param context context in which the constraint is evaluated
* @return {@code false} if {@code value} does not pass the constraint
*/
@Override
public boolean isValid(String country, ConstraintValidatorContext context) {
try {
return country == null && country.equals("US");
} catch (final Exception ex) {
//LOG.info("Error while getting values from object", ex);
return false;
}
}
}
@FieldMatch(first = "email", second = "confirmEmail", message = "Email does not match")
public class Person {
@NotNull
private String name;
private String email;
private String confirmEmail;
@CountryMatch(message = "Country should be US")
private String country;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getConfirmEmail() {
return confirmEmail;
}
public void setConfirmEmail(String confirmEmail) {
this.confirmEmail = confirmEmail;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
为了启动 Spring MVC JSR 验证,您需要在控制器中使用 @Valid
注释来注释模型
在这篇文章中,我们学习了如何使用 Spring MVC 自定义验证器。我们介绍了如何创建和使用类和字段级验证。
本文的所有代码都可用Over on Github。这是一个基于 Maven 的项目。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.javadevjournal.com/spring-mvc/spring-mvc-custom-validator/
内容来源于网络,如有侵权,请联系作者删除!