Spring Boot 应用程序中的验证可以通过多种不同的方式完成。根据您的要求,某些方法可能比其他方法更适合您的应用程序。在这篇文章中,我们将探讨在 Spring Boot 应用程序中验证数据的常用选项。
验证是通过使用Bean Validation API完成的。Bean Validation API 的参考实现是Hibernate Validator。
所有必需的依赖项都打包在 Spring Boot 启动器 POM
spring-boot-starter-validation 中。所以通常你需要开始的只是以下依赖:
<依赖>
org.springframework.boot
spring-boot-starter-validation
依赖>
验证约束是通过使用适当的 Bean 验证注释来注释字段来定义的。例如:
公共类地址{
@NotBlank
@大小(最大值 = 50)
私人弦街;
@NotBlank
@大小(最大值 = 50)
私人字符串城市;
@NotBlank
@大小(最大值 = 10)
私人字符串邮政编码;
@NotBlank
@大小(最大值 = 3)
私有字符串国家代码;
// getter + setter
}
我认为这些注释是不言自明的。我们将在以下许多示例中使用此地址类。
您可以在Bean Validation 文档中找到内置约束注释的完整列表。当然,您也可以通过创建自定义 ConstraintValidator来定义自己的验证约束。
定义验证约束只是其中的一部分。接下来我们需要触发实际的验证。这可以通过 Spring 或通过手动调用 Validator 来完成。我们将在下一节中看到这两种方法。
验证传入的请求数据使用 Spring Boot 构建 REST API 时,您可能想要验证传入的请求数据。这可以通过简单地将@Valid Annotation 添加到@RequestBody 方法参数来完成。例如:
@RestController
public class AddressController {
@PostMapping("/address")
public void createAddress(@Valid @RequestBody Address address) {
// ..
}
}
Spring 现在根据先前定义的约束自动验证传递的 Address 对象。
这种类型的验证通常用于确保客户端发送的数据在语法上是正确的。如果验证失败,则不会调用控制器方法,并向客户端返回 HTTP 400(错误请求)响应。更复杂的业务特定验证约束通常应稍后在业务层中检查。
持久层验证在 Spring Boot 应用程序中使用关系数据库时,您很可能也在使用 Spring Data 和 Hibernate。Hibernate 支持 Bean Validation。如果您的实体包含 Bean Validation 注释,则在持久化实体时会自动检查这些注释。
请注意,持久层绝对不应该是验证的唯一位置。如果此处验证失败,通常意味着其他应用程序组件中缺少某种验证。持久层验证应该被视为最后一道防线。除此之外,对于业务相关的验证,持久层通常为时已晚。
方法参数验证另一种选择是Spring 提供的方法参数验证。这允许我们向方法参数添加 Bean Validation 注解。然后 Spring 使用 AOP 拦截器在调用实际方法之前验证参数。
例如:
@Service
@Validated
public class CustomerService {
public void updateAddress(
@Pattern(regexp = "\\w{2}\\d{8}") String customerId,
@Valid Address newAddress
) {
// ..
}
}
这种方法对于验证进入服务层的数据很有用。但是,在采用这种方法之前,您应该了解它的局限性,因为这种类型的验证仅在涉及 Spring 代理时才有效。有关更多详细信息,请参阅我关于方法参数验证的单独帖子。
请注意,这种方法会使单元测试更加困难。为了测试服务中的验证约束,您现在必须引导 Spring 应用程序上下文。
以编程方式触发 Bean 验证在之前的验证解决方案中,实际的验证是由 Spring 或 Hibernate 触发的。但是,手动触发验证是非常可行的。这为我们将验证集成到应用程序的适当位置提供了极大的灵活性。
我们首先创建一个 ValidationFacade bean:
@Component
public class ValidationFacade {
private final Validator validator;
public ValidationFacade(Validator validator) {
this.validator = validator;
}
public void validate(T object, Class>... groups) {
Set> violations = validator.validate(object, groups);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
此 bean 接受 Validator 作为构造函数参数。Validator 是 Bean Validation API 的一部分,负责验证 Java 对象。Validator 的实例由 Spring 自动提供,因此可以将其注入到我们的 ValidationFacade 中。
在 validate(..) 方法中,我们使用 Validator 来验证传递的对象。结果是一组 ConstraintViolations。如果没有违反验证约束(= 对象有效),则 Set 为空。否则,我们抛出一个
ConstraintViolationException。
我们现在可以将 ValidationFacade 注入到其他 bean 中。例如:
@Service
public class CustomerService {
private final ValidationFacade validationFacade;
public CustomerService(ValidationFacade validationFacade) {
this.validationFacade = validationFacade;
}
public void updateAddress(String customerId, Address newAddress) {
validationFacade.validate(newAddress);
// ...
}
}
要验证一个对象(这里是 newAddress),我们只需调用 ValidationFacade 的 validate(..) 方法。当然,我们也可以直接在 CustomerService 中注入 Validator。但是,在验证错误的情况下,我们通常不想处理返回的 ConstraintViolations 集。相反,我们很可能只是想抛出一个异常,这正是 ValidationFacade 正在做的事情。
通常这是在服务/业务层进行验证的好方法。它不限于方法参数,可以与不同类型的对象一起使用。例如,我们可以从数据库中加载一个对象,对其进行修改,然后在继续之前对其进行验证。
这种方式也非常适合单元测试,因为我们可以简单地模拟 ValidationFacade。如果我们想在单元测试中进行真正的验证,可以手动创建所需的 Validator 实例(如下一节所示)。这两种情况都不需要在我们的测试中引导 Spring 应用程序上下文。
验证内部业务类另一种方法是在您的实际业务类中移动验证。在进行领域驱动设计时,这可能是一个很好的选择。例如,在创建 Address 实例时,构造函数可以确保我们无法构造无效对象:
public class Address {
@NotBlank
@Size(max = 50)
private String street;
@NotBlank
@Size(max = 50)
private String city;
...
public Address(String street, String city) {
this.street = street;
this.city = city;
ValidationHelper.validate(this);
}
}
这里构造函数调用一个静态的 validate(..) 方法来验证对象的状态。这个静态的 validate(..) 方法看起来类似于之前在 ValidationFacade 中显示的方法:
public class ValidationHelper {
private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public static void validate(T object, Class>... groups) {
Set> violations = validator.validate(object, groups);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
这里的区别是我们不通过 Spring 检索 Validator 实例。相反,我们使用以下方法手动创建它:
Validation.buildDefaultValidatorFactory().getValidator()
通过这种方式,我们可以将验证直接集成到域对象中,而无需依赖外部人员来验证对象。
概括我们看到了在 Spring Boot 应用程序中处理验证的不同方法。验证传入的请求数据有助于尽早拒绝废话。持久层验证只能用作额外的安全层。方法验证可能非常有用,但请确保您了解其局限性。即使以编程方式触发 Bean Validation 需要更多的努力,但它通常是最灵活的方式。
学习更多JAVA知识与技巧,关注与私信博主(学习)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)