1. Overview
1.概述
In this tutorial, we’ll discuss Spring validation in the service layer of a Java application. Although Spring Boot supports seamless integration with custom validators, the de-facto standard for performing validation is Hibernate Validator.
在本教程中,我们将讨论Java应用的服务层中的Spring验证。尽管Spring Boot支持与自定义验证器的无缝集成,但执行验证的事实标准是Hibernate Validator。
Here, we’ll learn how to move our validation logic out of our controllers and into a separate service layer. Additionally, we will implement validation in the service layer in a Spring application.
在这里,我们将学习如何将我们的验证逻辑从控制器中移出,转到独立的服务层。此外,我们将在Spring应用程序的服务层中实现验证。
2. Application Layering
2.应用分层
Depending on the requirements, Java business applications can take several different shapes and types. For instance, we must determine which layers our application requires based on these criteria. Unless there is a specific need, many applications would not benefit from the added complexity and maintenance costs of service or repository layers.
根据不同的要求,Java商业应用可以有几种不同的形状和类型。例如,我们必须根据这些标准来确定我们的应用程序需要哪些层。除非有特殊需要,否则许多应用程序不会从服务层或资源库层的额外复杂性和维护成本中受益。
We can fulfill all these concerns by using multiple layers. These layers are:
我们可以通过使用多个层次来实现所有这些关注。这些层是
The Consumer layer or Web layer is the topmost layer of a web application. It’s in charge of interpreting the user’s inputs and providing the appropriate response. The exceptions thrown by the other layers must also be handled by the web layer. Since the web layer is our application’s entry point, it’s responsible for authentication and serves as the first line of protection against unauthorized users.
消费者层或网络层是网络应用的最顶层。它负责解释用户的输入并提供适当的响应。其他层抛出的异常也必须由Web层来处理。由于Web层是我们应用程序的入口,它负责认证,并作为防止未授权用户的第一道保护线。
Under the web layer is the Service layer. It serves as a transactional barrier and houses both application and infrastructure services. Furthermore, the public API of the service layer is provided by the application services. They often serve as a transaction boundary and are in charge of authorizing transactions. Infrastructure services provide the “plumbing code” that connects to external tools including file systems, databases, and email servers. These approaches are often used by several application services.
网络层下面是服务层。它作为一个交易的屏障,容纳了应用和基础设施服务。此外,服务层的公共API是由应用服务提供的。它们通常充当交易边界,负责授权交易。基础设施服务提供 “管道代码”,连接到外部工具,包括文件系统、数据库和电子邮件服务器。这些方法经常被几个应用服务所使用。
A web application’s lowest layer is the persistence layer. In other words, it’s in charge of interacting with the user’s data storage.
Web应用程序的最低层是持久层。换句话说,它负责与用户的数据存储进行交互。
3. Validation in the Service Layer
3.服务层的验证
A service layer is a layer in an application that facilitates communication between the controller and the persistence layer. Additionally, business logic is stored in the service layer. It includes validation logic in particular. The model state is used to communicate between the controller and service layers.
服务层是应用程序中的一个层,用于促进控制器和持久层之间的通信。此外,业务逻辑被存储在服务层中。它特别包括验证逻辑。模型状态被用来在控制器和服务层之间进行通信。
There are advantages and disadvantages to treating validation as business logic, and Spring’s validation (and data binding) architecture does not preclude either. Validation, in particular, should not be bound to the web tier, should be simple to localize, and should allow for the use of any validator available.
将验证视为业务逻辑有利有弊,Spring的验证(和数据绑定)架构并不排除这两种可能性。尤其是验证,不应绑定到Web层,应简单地进行本地化,并应允许使用任何可用的验证器。
Also, client input data does not always pass through the REST controller process, and if we don’t validate in the Service layer as well, unacceptable data can pass through, causing several issues. In this case, we’ll use the standard Java JSR-303 validation scheme.
另外,客户端输入的数据并不总是通过REST控制器过程,如果我们不在服务层也进行验证,不可接受的数据就会通过,造成一些问题。在这种情况下,我们将使用标准的Java JSR-303验证方案。
4. Example
4.示例
Let’s consider a simple user account registration form developed using Spring Boot.
让我们考虑一下使用Spring Boot开发的一个简单的用户账户注册表。
4.1. Simple Domain Class
4.1.简单的域类
To begin with, we’ll have only the name, age, phone, and password attributes:
首先,我们将只有姓名、年龄、电话和密码属性。
public class UserAccount {
@NotNull(message = "Password must be between 4 to 15 characters")
@Size(min = 4, max = 15)
private String password;
@NotBlank(message = "Name must not be blank")
private String name;
@Min(value = 18, message = "Age should not be less than 18")
private int age;
@NotBlank(message = "Phone must not be blank")
private String phone;
// standard constructors / setters / getters / toString
}
Here in the above class, we’ve used four annotations – @NotNull, @Size, @NotBlank, and @Min – to make sure that the input attributes are neither null nor blank and adhere to the size requirements.
在上面的类中,我们使用了四个注解–@NotNull, @Size, @NotBlank, 和@Min–来确保输入属性既不是空的也不是空白的,并遵守大小要求。
4.2. Implementing Validation in Service Layer
4.2.在服务层实施验证
There are many validation solutions available, with Spring or Hibernate handling the actual validation. On the other hand, manual validation is a viable alternative. When it comes to integrating validation into the right part of our app, this gives us a lot of flexibility.
有许多验证解决方案可用,由Spring或Hibernate处理实际验证。另一方面,手动验证也是一种可行的选择。当涉及到将验证集成到我们应用程序的正确部分时,这给了我们很大的灵活性。
Next, let’s implement our validation in the service class:
接下来,让我们在服务类中实现我们的验证。
@Service
public class UserAccountService {
@Autowired
private Validator validator;
@Autowired
private UserAccountDao dao;
public String addUserAccount(UserAccount useraccount) {
Set<ConstraintViolation<UserAccount>> violations = validator.validate(useraccount);
if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<UserAccount> constraintViolation : violations) {
sb.append(constraintViolation.getMessage());
}
throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations);
}
dao.addUserAccount(useraccount);
return "Account for " + useraccount.getName() + " Added!";
}
}
Validator is part of the Bean Validation API and responsible for validating Java objects. Furthermore, Spring automatically provides a Validator instance, which we can inject into our UserAccountService. The Validator is used to validate a passed object within the validate(..) function. The result is a Set of ConstraintViolation.
Validator是Bean Validation API的一部分,负责验证Java对象。此外,Spring自动提供了一个Validator实例,我们可以将其注入我们的UserAccountService中。Validator用于验证validate(..)函数中传递的对象。其结果是一个Set的ConstraintViolation。
If no validation constraints are violated (the object is valid), the Set is empty. Otherwise, we throw a ConstraintViolationException.
如果没有违反验证约束(对象是有效的),Set是空的。否则,我们抛出一个ConstraintViolationException。
4.3. Implementing a REST Controller
4.3.实现一个REST控制器
After this, let’s build the Spring REST Controller class to display the service to the client or end-user and evaluate input validation for the application:
在这之后,让我们建立Spring REST控制器类,向客户端或最终用户显示服务,并评估应用程序的输入验证。
@RestController
public class UserAccountController {
@Autowired
private UserAccountService service;
@PostMapping("/addUserAccount")
public Object addUserAccount(@RequestBody UserAccount userAccount) {
return service.addUserAccount(userAccount);
}
}
We haven’t used the @Valid annotation in the above REST controller form to prevent any validation.
我们没有在上述REST控制器表单中使用@Valid注解,以防止任何验证。
4.4. Testing the REST Controller
4.4.测试REST控制器
Now, let’s test this method by running the Spring Boot application. After that, using Postman or any other API testing tool, we’ll post the JSON input to the localhost:8080/addUserAccount URL:
现在,让我们通过运行Spring Boot应用程序来测试这个方法。之后,使用Postman或其他API测试工具,我们将JSON输入发布到localhost:8080/addUserAccount URL。
{
"name":"Baeldung",
"age":25,
"phone":"1234567890",
"password":"test",
"useraddress":{
"countryCode":"UK"
}
}
{
"name":"",
"age":25,
"phone":"1234567890",
"password":"",
"useraddress":{
"countryCode":"UK"
}
}
Error occurred: Password must be between 4 to 15 characters, Name must not be blank
5. Pros and Cons
5.优点和缺点
In the service/business layer, this is often a successful approach for validation. It isn’t restricted to method parameters and can be applied to a variety of objects. We may, for example, load an object from a database, change it, and then validate it before proceeding.
在服务/业务层中,这通常是一种成功的验证方法。它并不局限于方法参数,可以应用于各种对象。例如,我们可以从数据库中加载一个对象,对其进行更改,然后在继续进行之前对其进行验证。
We can also use this method for unit tests so we can actually mock the Service class. In order to facilitate real validation in unit tests, we can manually generate the necessary Validator instance.
我们也可以将此方法用于单元测试,这样我们就可以真正模拟服务类。为了方便单元测试中的真实验证,我们可以手动生成必要的Validator实例。
Neither case requires bootstrapping a Spring application context in our tests.
这两种情况都不需要在我们的测试中引导Spring应用上下文。
6. Conclusion
6.结语
In this quick tutorial, we explored different layers of Java business applications. We learned how to move our validation logic out of our controllers and into a separate service layer. Furthermore, we implemented one approach to performing validation in the service layer of a Spring application.
在这个快速教程中,我们探索了Java商业应用的不同层次。我们学习了如何将我们的验证逻辑从控制器中转移到独立的服务层。此外,我们实现了一种在Spring应用程序的服务层执行验证的方法。
The code in the examples is available over on GitHub.
例子中的代码可在GitHub上找到。