在本文章中,我们将使用 Spring Boot 3.2 RestClient 类 进行 GET、POST、PUT 和 DELETE 调用 REST API。我们将首先使用 Spring Boot、Spring Data JPA 和 MySQL 数据库创建 CRUD REST API,然后使用 RestClient 类来使用这些 CRUD REST API。
RestClient类简介
RestClient 类 是 Spring Framework 6.1 和 Spring Boot 3.2 的新增内容。它提供了一个功能性风格的阻塞 HTTP API,其设计与 WebClient 类似。RestClient 的设计比 RestTemplate 更简单、更易于使用,与 RestTemplate 相比,它具有多个优势,包括:
- 流畅的 API,可以轻松地将请求链接在一起
- 支持注释来配置请求
- 内置错误处理
- 支持阻塞和非阻塞请求
1. 使用 Spring Boot、Spring Data JPA 和 MySQL 数据库构建 CRUD REST API
我们首先使用 Spring Boot、Spring Data JPA 和 MySQL 数据库创建 CRUD REST API,然后使用 RestClient 类来使用这些 CRUD REST API。
我们将在 Spring boot 项目中使用三层架构:
使用的工具和技术:
- Spring Boot 3.2
- Java 17
- Spring Data JPA
- Hibernate
- MySQL Database
- Maven
1.添加Maven依赖
Spring Boot 提供了一个名为Spring Initializer的 Web 工具来快速创建和引导 Spring Boot 应用程序。只需访问https://start.spring.io/并生成一个新的 spring boot 项目即可。
打开pom.xml文件并将以下 Maven 依赖项添加到项目中:
<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>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2.配置MySQL数据库
我们首先创建一个数据库,进入 MySQL Workbench,并使用以下 SQL 查询来创建一个新数据库:
create database employee_management
接下来,打开application.properties文件并向其中添加以下属性。
spring.datasource.url=jdbc:mysql://localhost:3306/employee_management
spring.datasource.username=root
spring.datasource.password=Mysql@123
# Hibernate properties
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
# create, create-drop
spring.jpa.hibernate.ddl-auto=update
确保根据 MySQL 安装更改spring.datasource.username和spring.datasource.password属性。
spring.jpa.hibernate.ddl-auto 属性用于数据库初始化。我已使用此属性的值“update”来自动创建数据库表。
3. 创建 Employee JPA 实体
实体是一个普通的 Java 对象 (POJO),它表示要存储的数据。您需要使用@Entity注释该类,并定义该类的字段以及每个字段的 getter 和 setter。让我们创建一个Employee类并向其中添加以下内容:
import jakarta.persistence.*;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false, unique = true)
private String email;
}
请注意,我们使用 Lombok 注释来减少样板代码(getters/setters)。
4.创建Spring Data JPA存储库-EmployeeRepository
存储库是一个接口,定义了在实体上执行 CRUD 操作的方法。Spring Data JPA 将自动创建 Repository 接口的实现。让我们创建一个扩展JpaRepository 的EmployeeRepository接口:
import net.javaguides.springboot.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
5.创建DTO类-EmployeeDto
让我们创建一个 EmployeeDto类来在客户端和服务器之间传输数据:
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class EmployeeDto {
private Long id;
private String firstName;
private String lastName;
private String email;
}
6. 创建服务层
该层将包含 API 的业务逻辑,并将用于使用存储库执行 CRUD 操作。让我们实现服务层,我们首先创建一个接口,然后创建它的实现类。
创建EmployeeService接口
让我们创建一个EmployeeService接口并声明以下 CRUD 方法:
import net.javaguides.springboot.dto.EmployeeDto;
import java.util.List;
public interface EmployeeService {
EmployeeDto createEmployee(EmployeeDto employeeDto);
EmployeeDto getEmployeeById(Long employeeId);
List<EmployeeDto> getAllEmployees();
EmployeeDto updateEmployee(EmployeeDto employeeDto);
void deleteEmployee(Long employeeId);
}
创建 UserServiceImpl 类
让我们创建一个EmployeeServiceImpl类来实现EmployeeService接口方法:
import lombok.AllArgsConstructor;
import net.javaguides.springboot.converter.EmployeeConverter;
import net.javaguides.springboot.dto.EmployeeDto;
import net.javaguides.springboot.entity.Employee;
import net.javaguides.springboot.service.EmployeeService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {
private EmployeeRepository employeeRepository;
@Override
public EmployeeDto createEmployee(EmployeeDto employeeDto) {
Employee employee = EmployeeConverter.mapToEmployee(employeeDto);
Employee savedEmployee = employeeRepository.save(employee);
return EmployeeConverter.mapToEmployeeDto(savedEmployee);
}
@Override
public EmployeeDto getEmployeeById(Long employeeId) {
// we need to check whether employee with given id is exist in DB or not
Employee existingEmployee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new IllegalArgumentException(
"Employee not exists with a given id : " + employeeId)
);
return EmployeeConverter.mapToEmployeeDto(existingEmployee);
}
@Override
public List<EmployeeDto> getAllEmployees() {
List<Employee> employees = employeeRepository.findAll();
return employees.stream()
.map(employee -> EmployeeConverter.mapToEmployeeDto(employee))
.collect(Collectors.toList());
}
@Override
public EmployeeDto updateEmployee(EmployeeDto employeeDto) {
// we need to check whether employee with given id is exist in DB or not
Employee existingEmployee = employeeRepository.findById(employeeDto.getId())
.orElseThrow(() -> new IllegalArgumentException(
"Employee not exists with a given id : " + employeeDto.getId())
);
// convert EmployeeDto to Employee JPA entity
Employee employee = EmployeeConverter.mapToEmployee(employeeDto);
return EmployeeConverter.mapToEmployeeDto(employeeRepository.save(employee));
}
@Override
public void deleteEmployee(Long employeeId) {
// we need to check whether employee with given id is exist in DB or not
Employee existingEmployee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new IllegalArgumentException(
"Employee not exists with a given id : " + employeeId)
);
employeeRepository.deleteById(employeeId);
}
}
7.创建控制器层-EmployeeController
现在,我们将创建用于创建、检索、更新和删除员工资源的 REST API。让我们创建一个EmployeeController类,并为Employee资源构建 CRUD REST API :
package net.javaguides.springboot.controller;
import lombok.AllArgsConstructor;
import net.javaguides.springboot.dto.EmployeeDto;
import net.javaguides.springboot.service.EmployeeService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@AllArgsConstructor
@RequestMapping("api/employees")
public class EmployeeController {
private EmployeeService employeeService;
// build create employee REST API
@PostMapping
public ResponseEntity<EmployeeDto> createEmployee(@RequestBody EmployeeDto employee){
EmployeeDto savedEmployee = employeeService.createEmployee(employee);
return new ResponseEntity<>(savedEmployee, HttpStatus.CREATED);
}
// build get employee by id REST API
// http://localhost:8080/api/employees/1
@GetMapping("{id}")
public ResponseEntity<EmployeeDto> getEmployeeById(@PathVariable("id") Long employeeId){
EmployeeDto employee = employeeService.getEmployeeById(employeeId);
//return new ResponseEntity<>(employee, HttpStatus.OK);
return ResponseEntity.ok(employee);
}
// build get all employees REST API
@GetMapping
public ResponseEntity<List<EmployeeDto>> getAllEmployees(){
List<EmployeeDto> employees = employeeService.getAllEmployees();
return new ResponseEntity<>(employees, HttpStatus.OK);
}
// build update employee REST API
// http://localhost:8080/api/employees/1
@PutMapping("{id}")
public ResponseEntity<EmployeeDto> updateEmployee(@PathVariable("id") long id
,@RequestBody EmployeeDto employeeDto){
employeeDto.setId(id);
EmployeeDto updatedEmployee = employeeService.updateEmployee(employeeDto);
return new ResponseEntity<EmployeeDto>(updatedEmployee, HttpStatus.OK);
}
// build delete employee REST API
// http://localhost:8080/api/employees/1
@DeleteMapping("{id}")
public ResponseEntity<String> deleteEmployee(@PathVariable("id") long id){
// delete employee from DB
employeeService.deleteEmployee(id);
return new ResponseEntity<String>("Employee deleted successfully!.", HttpStatus.OK);
}
}
8. 运行 Spring Boot 应用程序
从 IDE 中,独立运行包含main()方法的主入口点类。主入口点类将在端口 8080 上的嵌入式 Tomcat 服务器中启动 Spring Boot 项目。
2. 使用 RestClient 类使用 CRUD REST API
现在,我们将使用 Spring Boot 3.2 RestClient 类在我们在上一节中构建的 Employee 资源上使用 CRUD REST API。
1. 创建RestClient
要创建 RestClient,您可以使用 RestClientBuilder 类。RestClientBuilder 类允许您配置 RestClient,例如设置基本 URI、添加拦截器和配置超时。
public class RestClientTest {
private final RestClient restClient;
public RestClientTest() {
restClient = RestClient.builder()
.baseUrl("http://localhost:8080")
.build();
}
}
与RestTemplate或任何其他 Rest 客户端类似,RestClient 允许我们使用请求方法进行 HTTP 调用。让我们逐步了解创建、检索、修改和删除资源的不同 HTTP 方法。
2. 使用POST创建资源
我们使用 POST HTTP 方法将数据提交到 Web 服务器上的资源,通常是为了在 Web 应用程序中创建新资源。
让我们使用RestClient.post()方法发送 HTTP POST 请求来创建 Employee 资源:
@Test
public void createEmployee() {
EmployeeDto newEmployee = new EmployeeDto(null, "admin", "admin", "admin123@gmail.com");
EmployeeDto savedEmployee = restClient.post()
.uri("/api/employees")
.contentType(MediaType.APPLICATION_JSON)
.body(newEmployee)
.retrieve()
.body(EmployeeDto.class);
System.out.println(savedEmployee.toString());
}
2. 使用 GET 检索资源
让我们使用RestClient.get()方法发出 HTTP GET 请求来检索特定的 Employee:
@Test
public void getEmployeeById() {
Long employeeId = 1L;
EmployeeDto employeeDto = restClient.get()
.uri("/api/employees/{id}", employeeId)
.retrieve()
.body(EmployeeDto.class);
System.out.println(employeeDto);
}
3. 使用 UPDATE 更新现有资源
让我们使用 RestClient.put() 方法发出 HTTP PUT 请求来更新现有的 Employee 实体:
@Test
public void updateEmployee() {
Long employeeId = 1L;
EmployeeDto updatedEmployee = new EmployeeDto();
updatedEmployee.setFirstName("Ramesh");
updatedEmployee.setLastName("Fadatare");
updatedEmployee.setEmail("ramesh@gmail.com");
EmployeeDto result = restClient.put()
.uri("/api/employees/{id}", employeeId)
.contentType(MediaType.APPLICATION_JSON)
.body(updatedEmployee)
.retrieve()
.body(EmployeeDto.class);
System.out.println(result.toString());
}
5. 使用 GET 检索所有资源
让我们使用 RestClient.get() 方法发出 HTTP GET 请求来检索所有员工:
@Test
public void findAll() {
List<EmployeeDto> listOfEmployees = restClient.get()
.uri("/api/employees")
.retrieve()
.body(new ParameterizedTypeReference<List<EmployeeDto>>() {});
listOfEmployees.forEach(employeeDto -> {
System.out.println(employeeDto.toString());
});
}
6. 使用 DELETE 删除现有资源
让我们使用 RestClient.delete() 方法发出 HTTP DELETE 请求来删除特定的 Employee:
@Test
public void deleteEmployee() {
Long employeeId = 1L;
String response = restClient.delete()
.uri("/api/employees/{id}", employeeId)
.retrieve()
.body(String.class);
System.out.println(response);
}
完整代码
以下是使用RestClient 消费 CRUD REST API 的完整代码:
import net.javaguides.springboot.dto.EmployeeDto;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClient;
import java.util.List;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RestClientTest {
private final RestClient restClient;
public RestClientTest() {
restClient = RestClient.builder()
.baseUrl("http://localhost:8080")
.build();
}
@Order(1)
@Test
public void createEmployee() {
EmployeeDto newEmployee = new EmployeeDto(null, "admin", "admin", "admin123@gmail.com");
EmployeeDto savedEmployee = restClient.post()
.uri("/api/employees")
.contentType(MediaType.APPLICATION_JSON)
.body(newEmployee)
.retrieve()
.body(EmployeeDto.class);
System.out.println(savedEmployee.toString());
}
@Order(2)
@Test
public void getEmployeeById() {
Long employeeId = 1L;
EmployeeDto employeeDto = restClient.get()
.uri("/api/employees/{id}", employeeId)
.retrieve()
.body(EmployeeDto.class);
System.out.println(employeeDto);
}
@Order(3)
@Test
public void updateEmployee() {
Long employeeId = 1L;
EmployeeDto updatedEmployee = new EmployeeDto();
updatedEmployee.setFirstName("Ramesh");
updatedEmployee.setLastName("Fadatare");
updatedEmployee.setEmail("ramesh@gmail.com");
EmployeeDto result = restClient.put()
.uri("/api/employees/{id}", employeeId)
.contentType(MediaType.APPLICATION_JSON)
.body(updatedEmployee)
.retrieve()
.body(EmployeeDto.class);
System.out.println(result.toString());
}
@Order(4)
@Test
public void findAll() {
List<EmployeeDto> listOfEmployees = restClient.get()
.uri("/api/employees")
.retrieve()
.body(new ParameterizedTypeReference<List<EmployeeDto>>() {});
listOfEmployees.forEach(employeeDto -> {
System.out.println(employeeDto.toString());
});
}
@Order(5)
@Test
public void deleteEmployee() {
Long employeeId = 1L;
String response = restClient.delete()
.uri("/api/employees/{id}", employeeId)
.retrieve()
.body(String.class);
System.out.println(response);
}
}
输出:
7. 异常处理
RestClient 对于失败的请求抛出两种类型的异常:
- HttpClientErrorException:带有 4xx 响应代码
- HttpServerErrorException:响应代码为 5xx
7.1 处理 HttpClientErrorException 示例
@Test
public void exceptionHandlingClientErrorDemo(){
HttpClientErrorException thrown = Assertions.assertThrows(HttpClientErrorException.class,
() -> {
EmployeeDto employee = restClient.get()
.uri("/employees/404")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(EmployeeDto.class);
});
Assertions.assertEquals(404, thrown.getStatusCode().value());
}
7.2 处理HttpServerErrorException示例
@Test
public void exceptionHandlingServerErrorDemo(){
HttpServerErrorException thrown = Assertions.assertThrows(HttpServerErrorException.class,
() -> {
EmployeeDto employee = restClient.get()
.uri("/api/employees/500")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(EmployeeDto.class);
});
Assertions.assertEquals(500, thrown.getStatusCode().value());
}
GitHub 存储库
本教程的源代码可在 GitHub 存储库中找到: spring-boot-3.2-restclient-demo
https://github.com/RameshMF/spring-boot-3.2-restclient-demo
结论
RestClient 类是一个功能强大且易于使用的工具,用于在 Spring Boot 应用程序中发出 HTTP 请求。它是RestTemplate的绝佳替代品,非常适合现代 Spring 应用程序。
本文暂时没有评论,来添加一个吧(●'◡'●)