Spring Boot 应用程序中的验证

Spring Boot 应用程序中的验证,第1张

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知识与技巧,关注与私信博主(学习)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/757801.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-01
下一篇 2022-05-01

发表评论

登录后才能评论

评论列表(0条)

保存