在这篇文章中,我们将学习如何为使用Spring Boot开发的RESTful Web服务处理异常。
我们可以通过使用@ExceptionHandler
和@ControllerAdvice
注解来处理REST API中的异常,与我们在基于SpringMVC的Web应用中处理异常的方式相同。你可以返回ResponseEntity
,并附上适当的HTTP状态代码和异常细节,而不是渲染一个视图。
默认情况下,Spring Boot提供了一个/error
映射,以合理的方式处理所有错误,它被注册为Servlet容器中的 "全局 "错误页面。在客户端,它会产生一个JSON响应,包含错误的细节、HTTP状态和异常消息。
与其简单地抛出一个带有HTTP状态代码的异常,不如提供更多关于这个问题的细节,如错误代码、消息、原因等。在这个例子中,我们定义了一个用@ControllerAdvice
注解的类,以定制JSON文档,以返回特定的控制器和/或异常类型。
让我们使用Spring Boot 2+、JPA、Hibernate 5+、MySQL数据库,为Employee
resource开发一个CRUD REST APIs,我们将研究这些REST服务的异常处理。
我们将为Employee
资源开发一个简单的Spring Boot RESTful CRUD APIs,我们将为这些RESTful服务实现异常(错误)处理。
有很多方法可以创建Spring Boot应用程序。最简单的方法是在http://start.spring.io/使用Spring Initializr,它是一个在线Spring Boot应用程序生成器。
参考下面的pom.xml
f,供你参考。
<?xmlversion="1.0"encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.guides.springboot2</groupId>
<artifactId>springboot2-jpa-crud-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot2-jpa-crud-example</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
下一步是我们将创建JPA实体 - Employee.java
package net.guides.springboot2.springboot2jpacrudexample.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "employees")
public class Employee {
private long id;
private String firstName;
private String lastName;
private String emailId;
public Employee() {
}
public Employee(String firstName, String lastName, String emailId) {
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Column(name = "first_name", nullable = false)
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Column(name = "last_name", nullable = false)
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Column(name = "email_address", nullable = false)
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
@Override
public String toString() {
return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", emailId=" + emailId
+ "]";
}
}
接下来,我们将创建Spring Data JPA资源库 - EmployeeRepository.java
package net.guides.springboot2.springboot2jpacrudexample.model.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import net.guides.springboot2.springboot2jpacrudexample.model.Employee;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long>{
}
一旦实体和资源库到位,我们将创建Spring Rest Controller - EmployeeController.java
package net.guides.springboot2.springboot2jpacrudexample.model.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.guides.springboot2.springboot2jpacrudexample.model.Employee;
import net.guides.springboot2.springboot2jpacrudexample.model.exception.ResourceNotFoundException;
import net.guides.springboot2.springboot2jpacrudexample.model.repository.EmployeeRepository;
@RestController
@RequestMapping("/api/v1")
public class EmployeeController {
@Autowired
private EmployeeRepository employeeRepository;
@GetMapping("/employees")
public List<Employee> getAllEmployees() {
return employeeRepository.findAll();
}
@GetMapping("/employees/{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable(value = "id") Long employeeId)
throws ResourceNotFoundException {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));
return ResponseEntity.ok().body(employee);
}
@PostMapping("/employees")
public Employee createEmployee(@Valid @RequestBody Employee employee) {
return employeeRepository.save(employee);
}
@PutMapping("/employees/{id}")
public ResponseEntity<Employee> updateEmployee(@PathVariable(value = "id") Long employeeId,
@Valid @RequestBody Employee employeeDetails) throws ResourceNotFoundException {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));
employee.setEmailId(employeeDetails.getEmailId());
employee.setLastName(employeeDetails.getLastName());
employee.setFirstName(employeeDetails.getFirstName());
final Employee updatedEmployee = employeeRepository.save(employee);
return ResponseEntity.ok(updatedEmployee);
}
@DeleteMapping("/employees/{id}")
public Map<String, Boolean> deleteEmployee(@PathVariable(value = "id") Long employeeId)
throws ResourceNotFoundException {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));
employeeRepository.delete(employee);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return response;
}
}
到目前为止,我们已经为Employee
资源开发了CRUD RESTful APIs。现在我们将研究如何处理上述RESTFul APIs的异常或错误。
Spring Boot为RESTful服务的异常处理提供了一个良好的默认实现。
让我们快速了解一下Spring Boot提供的默认异常处理功能。
当你向一个未找到的资源发出请求时,会发生以下情况: http://localhost:8080/some-dummy-url
{
"timestamp": 1512713804164,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/some-dummy-url"
}
这是个很酷的错误响应。它包含所有通常需要的细节。
我们可以用@ResponseStatus
注解来指定特定异常的响应状态以及异常的定义,让我们看看Spring Boot在资源抛出异常时做了什么。
让我们创建一个ResourceNotFoundException.java
类。
package com.companyname.springbootcrudrest.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends Exception{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message){
super(message);
}
}
Spring Boot提供的默认错误响应包含所有通常需要的细节。
但是,你可能想为你的组织创建一个独立于框架的响应结构。在这种情况下,你可以定义一个特定的错误响应结构。
让我们来定义一个简单的错误响应Bean。
package com.companyname.springbootcrudrest.exception;
import java.util.Date;
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
super();
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
public Date getTimestamp() {
return timestamp;
}
public String getMessage() {
return message;
}
public String getDetails() {
return details;
}
}
为了使用ErrorDetails
来返回错误响应,让我们创建一个带有@ControllerAdvice
注解的GlobalExceptionHandler
类。这个类在一个地方处理特定异常和全局异常。
package com.companyname.springbootcrudrest.exception;
import java.util.Date;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> globleExcpetionHandler(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
我们已经完成了CRUD REST APIs和REST APIs异常处理的开发。现在是时候运行这个Spring boot应用程序了。
这个spring boot应用程序有一个名为Application.java
的入口点Java类,其中有*public static void main(String[] args)*方法,你可以运行它来启动该应用程序。
package net.guides.springboot2.springboot2jpacrudexample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
请注意,在上面的截图中,我们已经成功地进行了异常处理,并得到了来自REST APIs的预期响应。
这篇文章的源代码可以在我的GitHub仓库中找到。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.javaguides.net/2018/09/spring-boot-2-exception-handling-for-rest-apis.html
内容来源于网络,如有侵权,请联系作者删除!