# Spring REST 请求主体和参数验证示例

> [https://howtodoinjava.com/spring-restful/request-body-parameter-validation/](https://howtodoinjava.com/spring-restful/request-body-parameter-validation/)

学习**验证请求正文**（发布到 Spring REST API 的 JSON）。 还使用 [Hibernate 验证器 2.x](https://beanvalidation.org/2.0/) 验证资源 URI 中的`@PathVariable`和`@RequestParam`参数。

在此 **SpringRest 验证示例**中，我们将在为 [CRUD 示例](https://howtodoinjava.com/spring-restful/spring-rest-crud-jpa-example/)创建的 [REST API](http://restfulapi.net/) 中添加验证。

## 1\. 使用 Hibernate 验证器的请求正文验证

#### 1.1. Maven 依赖

`pom.xml`

```java
<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.0.16.Final</version>
</dependency>

```

这可传递地将对 Bean 验证 API 的依赖关系拉到`javax.validation:validation-api: 2.0.1.Final`。

#### 1.2. 启用 bean 验证

在 [SpringBoot](https://howtodoinjava.com/spring-boot-tutorials/) 中，如果类路径上有任何 **JSR-303** 实现（例如[Hibernate 验证器 2.0](https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/) ），则将自动启用 Bean 验证。

如果不使用 Spring Boot，则需要添加[`LocalValidatorFactoryBean`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.html)。

`Java 配置`

```java
@Bean
public javax.validation.Validator localValidatorFactoryBean() {
	return new LocalValidatorFactoryBean();
}

```

`XML 配置`

```java
<bean id="validator" 
	class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

```

#### 1.3. 添加 bean 验证注解

在模型类中添加 Bean 验证注解，该注解将存储请求正文数据，例如`@NotEmpty`和`@Email`。

`Java 配置`

```java
import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;

@Entity
@Table(name = "tbl_employee")
public class Employee implements Serializable
{
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@NotEmpty(message = "First name must not be empty")
	private String firstName;

	@NotEmpty(message = "Last name must not be empty")
	private String lastName;

	@NotEmpty(message = "Email must not be empty")
	@Email(message = "Email must be a valid email address")
	private String email;

	public Employee() {
	}

	//setters and getters

	@Override
	public String toString() {
	    return "EmployeeVO [id=" + id + ", firstName=" + firstName + ", 
	            lastName=" + lastName + ", email=" + email + "]";
	}
}

```

#### 1.4. 处理`ConstraintViolationException`

如果发生任何验证失败，Spring 将抛出[`ConstraintViolationException`](https://javaee.github.io/javaee-spec/javadocs/javax/validation/ConstraintViolationException.html)。 我们可以使用[`@ExceptionHandler`](https://howtodoinjava.com/spring-core/spring-exceptionhandler-annotation/)处理任何返回有意义的 JSON 错误响应的事件。

`CustomExceptionHandler.java`

```java
@ControllerAdvice
@ResponseBody
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
	@ExceptionHandler(ConstraintViolationException.class)
	public final ResponseEntity<ErrorResponse> handleConstraintViolation(
											ConstraintViolationException ex,
											WebRequest request)
	{
		List<String> details = ex.getConstraintViolations()
									.parallelStream()
									.map(e -> e.getMessage())
									.collect(Collectors.toList());

		ErrorResponse error = new ErrorResponse(BAD_REQUEST, details);
		return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
	}
}

```

## 2\. 查询和路径参数验证

在 Spring REST 中，通过`@PathVariable`捕获请求 URI 中的参数，并通过`@RequestParam`捕获所有查询参数。

请注意，必须添加 maven 依赖项，并且应按上述说明处理`ConstraintViolationException`。

#### 2.1. 启用验证

查询和路径参数验证并不简单。 我们需要显式创建 bean `MethodValidationPostProcessor`，它将处理`@Validated`注解。

`XML 配置`

```java
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

```

`Java 配置`

```java
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
     return new MethodValidationPostProcessor();
}

```

#### 2.2. 向参数添加验证

*   使用如上所述的 JSR-303 注解。
*   在控制器顶部使用`@Validated`注解，因此适用于其中的所有方法。

```java
@RestController
@RequestMapping(value = "/employee-management", 
		produces = { MediaType.APPLICATION_JSON_VALUE })
@Validated
public class EmployeeRESTController 
{
	@GetMapping("/employees/{id}")
	Employee getEmployeeById(@PathVariable 
							 @Min(value = 1, message = "id must be greater than or equal to 1") 
							 @Max(value = 1000, message = "id must be lower than or equal to 1000") Long id)
	{
	    return repository.findById(id)
	            .orElseThrow(() -> new RecordNotFoundException("Employee id '" + id + "' does no exist"));
	}
}

```

## 3\. 演示

#### 3.1. 请求正文验证


`API 请求 1`

```java
HTTP POST : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/

Headers:

AUTH_API_KEY: abcd123456
Content-Type: application/json

Body:

{
    "firstName": "",
    "lastName": "Gupta",
    "email": "abc@gmail.com"
}

```

`API 响应 1`

```java
{
	"message":"BAD_REQUEST",
	"details":["First name must not be empty"]
}

```


`API 请求 2`

```java
HTTP POST : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/

Headers:

AUTH_API_KEY: abcd123456
Content-Type: application/json

Body:

{
    "firstName": "",
    "email": "abc@gmail.com"
}

```

`API 响应 2`

```java
{
	"message":"BAD_REQUEST",
	"details": 
			[
				"First name must not be empty",
				"Last name must not be empty"
			]
}

```

#### 3.2. 路径参数验证


`API 请求 1`

```java
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/0

Headers:

AUTH_API_KEY: abcd123456
Content-Type: application/json

```

`API 响应 1`

```java
{
	"message":"BAD_REQUEST",
	"details":["id must be greater than or equal to 1"]
}
```

`API 请求 2`

```java
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/5000

Headers:

AUTH_API_KEY: abcd123456
Content-Type: application/json

```

`API 请求 2`


```java
{
“message”:”BAD_REQUEST”,
“details”:[“id must be lower than or equal to 1000”]
}
```

[下载源码](https://howtodoinjava.com/wp-content/downloads/SpringRestExample.zip)

学习愉快！