原文: https://howtodoinjava.com/spring-boot2/spring-rest-request-validation/
在本SpringBoot 异常处理器教程中,我们将学习验证发送到 PUT / POST REST API 的请求正文。 我们还将学习在验证错误的 API 响应中添加自定义错误消息。
在这个 SpringBoot 示例中,我们将主要看到两个主要的验证案例:
- HTTP POST
/employees
和请求正文不包含有效值,或者某些字段丢失。 它将在响应正文中返回 HTTP 状态代码 400,并带有正确的消息。 - HTTP GET
/employees/{id}
和无效 ID 在请求中发送。 它将在响应正文中返回带有正确消息的 HTTP 状态代码 404。
阅读更多: HTTP 状态代码
1. 创建 REST API 和模型类
给定的 REST API 来自员工管理模块。
EmployeeRESTController.java
@PostMapping(value = "/employees")
public ResponseEntity<EmployeeVO> addEmployee (@RequestBody EmployeeVO employee)
{
EmployeeDB.addEmployee(employee);
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
@GetMapping(value = "/employees/{id}")
public ResponseEntity<EmployeeVO> getEmployeeById (@PathVariable("id") int id)
{
EmployeeVO employee = EmployeeDB.getEmployeeById(id);
if(employee == null) {
throw new RecordNotFoundException("Invalid employee id : " + id);
}
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
EmployeeVO.java
@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.FIELD)
public class EmployeeVO extends ResourceSupport implements Serializable
{
private Integer employeeId;
private String firstName;
private String lastName;
private String email;
public EmployeeVO(Integer id, String firstName, String lastName, String email) {
super();
this.employeeId = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public EmployeeVO() {
}
//Removed setter/getter for readability
}
2. Spring Boot 异常处理 – REST 请求验证
2.1. 默认的 Spring 验证支持
要应用默认验证,我们只需要在适当的位置添加相关注解即可。 即
使用所需的验证特定注解(例如
@NotEmpty
,@Email
等)注解模型类。@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.FIELD)
public class EmployeeVO extends ResourceSupport implements Serializable
{
private static final long serialVersionUID = 1L;
public EmployeeVO(Integer id, String firstName, String lastName, String email) {
super();
this.employeeId = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public EmployeeVO() {
}
private Integer employeeId;
@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 should be a valid email")
private String email;
//Removed setter/getter for readability
}
- 通过
@Valid
注解启用验证请求正文@PostMapping(value = "/employees")
public ResponseEntity<EmployeeVO> addEmployee (@Valid @RequestBody EmployeeVO employee)
{
EmployeeDB.addEmployee(employee);
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
2.2. 异常模型类
默认的 Spring 验证有效并提供有关错误的信息过多,这就是为什么我们应根据应用程序的需要对其进行自定义。 我们将仅提供措辞非常明确的必要错误信息。 不建议提供其他信息。
创建有意义的异常并充分描述问题始终是一个很好的建议。 一种方法是创建单独的类来表示特定的业务用例失败,并在该用例失败时返回它们。
阅读更多: Java 异常处理 – 新的应用程序
例如我为所有通过 ID 要求资源但在系统中找不到资源的场景创建了RecordNotFoundException
类。
RecordNotFoundException.java
package com.howtodoinjava.demo.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class RecordNotFoundException extends RuntimeException
{
public RecordNotFoundException(String exception) {
super(exception);
}
}
同样,我编写了一个特殊的类,将为所有失败情况返回该类。 所有 API 具有一致的错误消息结构,可帮助 API 使用者编写更健壮的代码。
ErrorResponse.java
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "error")
public class ErrorResponse
{
public ErrorResponse(String message, List<String> details) {
super();
this.message = message;
this.details = details;
}
//General error message about nature of error
private String message;
//Specific errors in API request processing
private List<String> details;
//Getter and setters
}
2.3. 自定义ExceptionHandler
现在添加一个扩展ResponseEntityExceptionHandler
的类,并使用@ControllerAdvice
注解对其进行注解。
ResponseEntityExceptionHandler
是一个方便的基类,用于通过@ExceptionHandler
方法跨所有@RequestMapping
方法提供集中式异常处理。 @ControllerAdvice
更多用于在应用程序启动时启用自动扫描和配置。
用于@ControllerAdvice
异常处理示例的 Java 程序。
CustomExceptionHandler.java
package com.howtodoinjava.demo.exception;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@SuppressWarnings({"unchecked","rawtypes"})
@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
@ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse("Server Error", details);
return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(RecordNotFoundException.class)
public final ResponseEntity<Object> handleUserNotFoundException(RecordNotFoundException ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse("Record Not Found", details);
return new ResponseEntity(error, HttpStatus.NOT_FOUND);
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
List<String> details = new ArrayList<>();
for(ObjectError error : ex.getBindingResult().getAllErrors()) {
details.add(error.getDefaultMessage());
}
ErrorResponse error = new ErrorResponse("Validation Failed", details);
return new ResponseEntity(error, HttpStatus.BAD_REQUEST);
}
}
上面的类处理多个异常,包括RecordNotFoundException
; 并且还可以处理@RequestBody
带注解的对象中的请求验证错误。 让我们看看它是如何工作的。
3. Spring Boot 异常处理 – 演示
1)HTTP GET /employees/1
[有效]
HTTP Status : 200
{
"employeeId": 1,
"firstName": "John",
"lastName": "Wick",
"email": "howtodoinjava@gmail.com",
}
2)HTTP GET /employees/23
[无效]
HTTP Status : 404
{
"message": "Record Not Found",
"details": [
"Invalid employee id : 23"
]
}
3)HTTP POST /employees
[无效]
Request
{
"lastName": "Bill",
"email": "ibill@gmail.com"
}
Response
HTTP Status : 400
{
"message": "Validation Failed",
"details": [
"first name must not be empty"
]
}
4)HTTP POST /employees
[无效]
Request
{
"email": "ibill@gmail.com"
}
Response
HTTP Status : 400
{
"message": "Validation Failed",
"details": [
"last name must not be empty",
"first name must not be empty"
]
}
5)HTTP POST /employees
[无效]
Request
{
"firstName":"Lokesh",
"email": "ibill_gmail.com" //invalid email in request
}
Response
HTTP Status : 400
{
"message": "Validation Failed",
"details": [
"last name must not be empty",
"email should be a valid email"
]
}
4. REST 请求验证注解
在上面的示例中,我们仅使用了很少的注解,例如@NotEmpty
和@Email
。 还有更多此类注解可用于验证请求数据。 必要时将其签出。
注解 | 用法 |
---|---|
@AssertFalse |
带注解的元素必须为false 。 |
@AssertTrue |
带注解的元素必须为true 。 |
@DecimalMax |
带注解的元素必须是一个数字,其值必须小于或等于指定的最大值。 |
@DecimalMin |
带注解的元素必须是一个数字,其值必须大于或等于指定的最小值。 |
@Future |
带注解的元素必须是将来的瞬间,日期或时间。 |
@Max |
带注解的元素必须是一个数字,其值必须小于或等于指定的最大值。 |
@Min |
带注解的元素必须是一个数字,其值必须大于或等于指定的最小值。 |
@Negative |
带注解的元素必须是严格的负数。 |
@NotBlank |
带注解的元素不能为null ,并且必须至少包含一个非空白字符。 |
@NotEmpty |
带注解的元素不能为null ,也不能为空。 |
@NotNull |
带注解的元素不能为null 。 |
@Null |
带注解的元素必须为null 。 |
@Pattern |
带注解的CharSequence 必须与指定的正则表达式匹配。 |
@Positive |
带注解的元素必须是严格的正数。 |
@Size |
带注解的元素大小必须在指定的边界(包括在内)之间。 |
5. 总结
在此Spring REST 验证教程中,我们学习了:
- 通过 ID 提取资源时验证 ID。
- 验证 POST / PUT API 中的请求正文字段。
- 在 API 响应中发送一致且结构化的错误响应。
将有关 spring rest 异常处理的问题交给我。
学习愉快!