Spring Boot RestTemplate介绍

x33g5p2x  于2022-09-17 转载在 Spring  
字(17.2k)|赞(0)|评价(0)|浏览(387)

在Spring Boot的这篇文章中,我们将检查Spring Boot RestTemplate或Spring RestTemplate。它使我们很容易消费和创建一个Spring Boot RESTful网络服务。

Spring Boot RestTemplate

消费和创建一个Spring Boot RESTful网络服务需要大量的模板代码。Spring Boot RestTemplate使创建和消费RESTfulweb服务变得容易。RestTemplate是一个Spring REST客户端,我们可以用它来消费不同的REST APIs。
Spring Framework 5开始,与WebFlux栈一起,Spring引入了一个新的HTTP客户端,称为WebClient。

WebClient是一个现代的、替代RestTemplate的HTTP客户端。它不仅提供了传统的同步API,而且还支持高效的非阻塞和异步方法。我们的建议是使用新的WebClient。他们将在未来的版本中废弃RestTemplate。

1.Introduction

RESTful Web服务主要代表Representational State Transfer。它基本上是一种架构风格,用于指定约束。这些约束包括一个统一的接口,引导网络服务诱发理想的属性,如性能和可伸缩性。简而言之,RESTful网络服务有助于使用户更容易接触到一个应用程序。

REST架构风格将数据和功能视为资源,并通过使用统一资源识别码(也称为URI)来访问这些资源。这些主要是连接它与网络的连接。它还包括一个具有客户/服务器连接的架构,主要是为了提供一个无状态的通信协议。我们基本上是通过使用这种标准化的接口和协议来执行客户和服务器交换资源的表示。一些用于数据交换/传输的主要协议有GET、POST、PUT、DELETE等。

1.1. RESTful应用的原则

RESTful应用程序有一些原则,使应用程序简单、轻量、快速。这些原则是。

**通过URI识别资源:**我们可以通过使用RESTful Web Service暴露资源。这些有助于目标与客户的互动。资源是由URI识别的,它为服务发现提供了一个全局寻址空间。一些用于暴露资源的注解是 @Path, @RequestMapping.
**统一的接口:**资源可以通过使用CRUD操作来操作。这些CRUD操作代表了创建(Create)、读取(Read)、更新(Update)和删除(Delete),即POST用于添加新的资源,我们使用GET来读取已经可用的资源,我们可以使用PUT来更新资源,而DELETE可以用于删除资源。所有这些都是HTTP方法,使用特定的请求来执行操作。
**描述信息:**资源或内容可以用多种方式访问,如HTML、XML、纯文本、PDF、JPEG、JSON等。我们还可以使用这些RESTful网络服务中的元数据,以提供控制缓存、传输错误、认证、授权控制等。
**在超链接的帮助下提供有状态的交互:**这些资源所提供的交互是无状态的,即请求信息和主体是自成一体的。它们是基于显式状态转移的概念。我们还可以将这些状态嵌入到响应消息中,以指向交互的有效的未来状态。

在Spring Boot RestTemplate的帮助下,我们可以通过使用上述RESTful Web服务功能来创建应用程序。我们可以使用exchange()方法,可以为所有的HTTP方法消费这些Web服务。

2. Maven的依赖性

在继续进行该项目之前,我们需要在系统中下载并安装以下先决条件。

  1. JDK应该安装在你的电脑上。
  2. 使用任何IDE进行开发,如IntelliJ、Eclipse、NetBeans。
  3. 应安装Postman以测试API调用。

在创建REST模板时,你不需要任何特殊的依赖性。这将需要与创建任何RESTful Web服务所需的相同的依赖项。以下是创建Web服务所需的依赖性。

<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>
</dependencies>

3. 项目设置

我们将创建一个spring多模块项目来展示两个微服务是如何交互的,以及我们如何使用RestTemplate从另一个服务中调用一个服务的rest端点。为了启动我们的项目,我们可以使用Spring initializr。我们将创建一个父模块,它将包含两个模块共有的依赖,如Lombok、spring-boot-starter-test等。下面提供pom.xml供参考。


Spring Boot RestTemplate项目设置

这就是我们在IDE中导入后的项目结构。

这里是我们的Spring Boot RestTemplate例子的完整pom.xml

<?xml version="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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.6.3</version>
      <relativePath />
      <!-- lookup parent from repository -->
   </parent>
   <groupId>com.javadevjournal</groupId>
   <artifactId>spring-boot-resttemplate</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>spring-boot-resttemplate</name>
   <description>Spring Boot project for RestTemplate illustration</description>
   <properties>
      <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>com.h2database</groupId>
         <artifactId>h2</artifactId>
         <scope>runtime</scope>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
      <dependency>
         <groupId>org.apache.httpcomponents</groupId>
         <artifactId>httpclient</artifactId>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
               <excludes>
                  <exclude>
                     <groupId>org.projectlombok</groupId>
                     <artifactId>lombok</artifactId>
                  </exclude>
               </excludes>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

为了了解如何使用Spring Boot RestTemplate,让我们创建运行应用程序所需的不同层的应用程序。这个项目将包括以下几层。控制器、服务、域和应用层。

3.1. 应用层

在应用层,我们有SpringbootRestTemplateApplication类,它被注解为@SpringBootApplication注解。这个注解标志着该类是一个配置类,我们可以在其中声明一个或多个@Bean方法。它还会触发自动配置和组件扫描。我们使用Spring Boot的@Bean注解,将RestTemplate Bean注入我们的应用程序中。  我们将在我们的控制器层@Autowire它。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class SpringbootRestTemplateApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootRestTemplateApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
3.2. 域对象

接下来,我们将创建我们的域层。我们将创建一个名为Employee的POJO类,它将是我们的DTO(数据传输对象)。@Data注解来自项目Lombok,它生成了锅炉板代码,如getters、setters、toString()、no-argument构造器。

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Employee implements Serializable {

    private static final long serialVersionUID = 1 L;

    int id;
    String name;
    Double salary;
}
  • @AllArgsConstructor -这是一个Lombok注解,用于生成一个构造函数将雇员类的所有成员变量。
  • @NoArgsConstructor-这是一个Lombok注解,它将为Employee类生成一个空的构造函数。
  • @Data - 这是一个Lombok注解,为Employee类的所有成员变量生成getters和setters。
3.3. 控制器层

我们在这个项目中创建了两个控制器。一个是EmployeeController,包含REST端点,对Employee资源进行CRUD操作。第二个是RestTemplateController,它是所有用户请求的处理者。它使用RestTemplate bean提供的不同方法来调用EmployeeController的REST APIs。

下面是我们的控制器类的代码,我们将分步理解。我们使用@Autowired注解在控制器类中注入RestTemplate的依赖性。如果我们有不同配置的RestTemplate,我们可以使用<a href="https://www.javadevjournal.com/spring/qualifier-annotation/" target="_blank" data-type="post" data-id="3741" rel="noreferrer noopener">@Qualifier annotation</a>

@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @GetMapping("/employees")
    private List getAllEmployees() {
        return employeeService.getAllEmployees();
    }

    @GetMapping("/employees/{id}")
    private Employee getEmployeeById(@PathVariable("id") int id) {
        return employeeService.getEmployeeById(id);
    }

    @PostMapping("/employees")
    private Employee createEmployee(@RequestBody Employee employee) {
        employeeService.saveOrUpdate(employee);
        return employee;
    }

    @PutMapping("/employees/{id}")
    private Employee updateEmployee(@PathVariable("id") int id, @RequestBody Employee employee) {
        Employee updatedEmployee = employeeService.getEmployeeById(id);
        updatedEmployee.setName(employee.getName());
        updatedEmployee.setSalary(employee.getSalary());
        employeeService.saveOrUpdate(updatedEmployee);
        return updatedEmployee;
    }

    @DeleteMapping("/employees/{id}")
    private Employee deleteById(@PathVariable("id") int id) {
        Employee employeeDeleted = employeeService.getEmployeeById(id);
        employeeService.delete(id);
        return employeeDeleted;
    }
}
  1. **@RestController-**这个注解将雇员控制器类标记为RestController。一旦这个注解被初始化,它将处理所有传入和传出的HTTP请求。

  2. 我们为下面的RestTemplate控制器创建了一个单独的类。这个类将从Employee Controller中调用HTTP方法,并在从Employee Controller中获取响应后返回结果。

  3. @RequestMapping() - 该注解添加了需要访问资源的HTTP API路径。

  4. 我们在上面的控制器类中创建了4个方法,基本上可以返回所需的响应。

  5. 1.GET

  6. POST

  7. PUT

  8. DELETE

#RestTemplateController.java
@RestController
public class RestTemplateController {

    private final String URI_EMPLOYEE = "http://localhost:8081/employees/";
    private final String URI_EMPLOYEE_ID = "http://localhost:8081/employees/{id}";

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/v1/allEmployees")
    public ResponseEntity getAllV1() {
        Employee[] EmployeesArray = restTemplate.getForObject(URI_EMPLOYEE, Employee[].class);
        return new ResponseEntity < > (Arrays.asList(EmployeesArray), HttpStatus.OK);
    }

    @GetMapping("/v1/employees/{id}")
    public ResponseEntity getByIdV1(@PathVariable final Integer id) {
        Map < String, String > params = new HashMap < > ();
        params.put("id", String.valueOf(id));
        Employee Employee = restTemplate.getForObject(URI_EMPLOYEE_ID, Employee.class, params);
        return new ResponseEntity < > (Employee, HttpStatus.OK);
    }

    @GetMapping("/v2/allEmployees")
    public ResponseEntity getAllV2() {
        ResponseEntity < Employee[] > responseEntity = restTemplate.getForEntity(URI_EMPLOYEE, Employee[].class);
        return responseEntity;
    }

    @GetMapping("/v2/employees/{id}")
    public ResponseEntity getByIdV2(@PathVariable final Integer id) {
        Map < String, String > params = new HashMap < > ();
        params.put("id", String.valueOf(id));
        ResponseEntity < Employee > responseEntity = restTemplate.getForEntity(URI_EMPLOYEE_ID, Employee.class, params);
        return responseEntity;
    }

    @GetMapping("/v3/allEmployees")
    public ResponseEntity getAllV3() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        HttpEntity < String > entity = new HttpEntity < > (httpHeaders);
        return restTemplate.exchange(URI_EMPLOYEE, HttpMethod.GET, entity, Employee[].class);
    }

    @GetMapping("/v3/employees/{id}")
    public ResponseEntity getByIdV3(@PathVariable final Integer id) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        HttpEntity < String > entity = new HttpEntity < > (httpHeaders);
        return restTemplate.exchange(URI_EMPLOYEE + id, HttpMethod.GET, entity, Employee.class);
    }

    @PostMapping("/v1/employees")
    public ResponseEntity createV1(@RequestBody final Employee newEmployee) {
        Employee createdEmployee = restTemplate.postForObject(URI_EMPLOYEE, newEmployee, Employee.class);
        return new ResponseEntity(createdEmployee, HttpStatus.CREATED);
    }

    @PostMapping("/v2/employees")
    public ResponseEntity createV2(@RequestBody final Employee newEmployee) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        HttpEntity < Employee > entity = new HttpEntity < > (newEmployee, httpHeaders);
        return restTemplate.exchange(URI_EMPLOYEE, HttpMethod.POST, entity, Employee.class);
    }

    @PutMapping("/v1/employees/{id}")
    public ResponseEntity updateEmployeeV1(@PathVariable final Integer id, @RequestBody Employee newEmployee) {
        Map < String, String > params = new HashMap < > ();
        params.put("id", String.valueOf(id));
        restTemplate.put(URI_EMPLOYEE_ID, newEmployee, params);
        return new ResponseEntity("Employee Updated with id " + id, HttpStatus.OK);
    }

    @PutMapping("/v2/employees/{id}")
    public ResponseEntity updateEmployeeV2(@PathVariable final Integer id, @RequestBody Employee newEmployee) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        HttpEntity < Employee > entity = new HttpEntity < > (newEmployee, httpHeaders);
        return restTemplate.exchange(URI_EMPLOYEE + id, HttpMethod.PUT, entity, Employee.class);
    }

    @DeleteMapping("/v1/employees/{id}")
    public ResponseEntity deleteV1(@PathVariable final Integer id) {
        Map < String, String > params = new HashMap < > ();
        params.put("id", String.valueOf(id));
        restTemplate.delete(URI_EMPLOYEE_ID, params);
        return new ResponseEntity < > ("Employee deleted with id " + id, HttpStatus.OK);
    }

    @DeleteMapping("/v2/employees/{id}")
    public ResponseEntity < Employee > deleteV2(@PathVariable final Integer id) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        HttpEntity < Employee > entity = new HttpEntity < > (httpHeaders);
        return restTemplate.exchange(URI_EMPLOYEE + id, HttpMethod.DELETE, entity, Employee.class);
    }
}

4. 方法API

Spring Boot RestTemplate为调用GET API提供了3种类型的方法。

4.1. GET API

getForObject()将发射一个GET请求并直接返回资源对象。在代码中,我们可以将输出包在ResponseEntity对象中,或者只是简单地返回资源对象的原样。当我们想把响应直接映射到资源的DTO时,我们会使用它。

@GetMapping("/v1/allEmployees")
public ResponseEntity getAllV1() {
    Employee[] EmployeesArray = restTemplate.getForObject(URI_EMPLOYEE, Employee[].class);
    return new ResponseEntity < > (Arrays.asList(EmployeesArray), HttpStatus.OK);
}

@GetMapping("/v1/employees/{id}")
public ResponseEntity getByIdV1(@PathVariable final Integer id) {
    Map < String, String > params = new HashMap < > ();
    params.put("id", String.valueOf(id));
    Employee Employee = restTemplate.getForObject(URI_EMPLOYEE_ID, Employee.class, params);
    return new ResponseEntity < > (Employee, HttpStatus.OK);
}

getForEntity()**将发射一个GET请求并返回ResponseEntity,其中包含状态码和资源对象。当我们想获得像JSON一样的响应时,我们可以使用它。

@GetMapping("/v1/allEmployees")
public ResponseEntity getAllV1() {
    Employee[] EmployeesArray = restTemplate.getForObject(URI_EMPLOYEE, Employee[].class);
    return new ResponseEntity < > (Arrays.asList(EmployeesArray), HttpStatus.OK);
}

@GetMapping("/v1/employees/{id}")
public ResponseEntity getByIdV1(@PathVariable final Integer id) {
    Map < String, String > params = new HashMap < > ();
    params.put("id", String.valueOf(id));
    Employee Employee = restTemplate.getForObject(URI_EMPLOYEE_ID, Employee.class, params);
    return new ResponseEntity < > (Employee, HttpStatus.OK);
}

exchange()方法是最通用的API,它可以调用一个GET/POST/PUT/DELETE请求。API的输入是URI、请求方法、包含头信息和实体代码(如果有的话)的请求HttpEntity,以及响应类型类。输出是一个ResponseEntity对象,包含状态代码和资源作为一个主体。

@GetMapping("/v3/allEmployees")
public ResponseEntity getAllV3() {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity < String > entity = new HttpEntity < > (httpHeaders);
    return restTemplate.exchange(URI_EMPLOYEE, HttpMethod.GET, entity, Employee[].class);
}

@GetMapping("/v3/employees/{id}")
public ResponseEntity getByIdV3(@PathVariable final Integer id) {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity < String > entity = new HttpEntity < > (httpHeaders);
    return restTemplate.exchange(URI_EMPLOYEE + id, HttpMethod.GET, entity, Employee.class);
}
4.2. POST API

Spring Boot RestTemplate为调用POST API提供了4种方法。让我们来看看每一种方法,以便弄清楚。

  1. postForLocation()-它将启动一个POST请求,它将接受URI、雇员请求体,并返回新创建资源的位置。
  2. postForEntity() - 它将发起一个POST请求,接收URI、雇员请求体和responseType作为输入,并以JSON格式返回资源。
  3. postForObject()-它将发起一个POST请求,将URI、雇员请求体和responseType作为输入并返回资源对象。
@PostMapping("/v1/employees")
public ResponseEntity createV1(@RequestBody final Employee newEmployee) {
    Employee createdEmployee = restTemplate.postForObject(URI_EMPLOYEE, newEmployee, Employee.class);
    return new ResponseEntity(createdEmployee, HttpStatus.CREATED);
}

The**exchange()**API

@PostMapping("/v2/employees")
public ResponseEntity createV2(@RequestBody final Employee newEmployee) {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity < Employee > entity = new HttpEntity < > (newEmployee, httpHeaders);
    return restTemplate.exchange(URI_EMPLOYEE, HttpMethod.POST, entity, Employee.class);
}
4.3. DELETE API

Spring Boot RestTemplate提供了2种类型的方法来调用DELETE API。delete()方法将发射一个删除请求。让我们看看一些代码例子,以便更清楚地了解。

@DeleteMapping("/v1/employees/{id}")
public ResponseEntity deleteV1(@PathVariable final Integer id) {
    Map < String, String > params = new HashMap < > ();
    params.put("id", String.valueOf(id));
    restTemplate.delete(URI_EMPLOYEE_ID, params);
    return new ResponseEntity < > ("Employee deleted with id " + id, HttpStatus.OK);
}

@DeleteMapping("/v2/employees/{id}")
public ResponseEntity < Employee > deleteV2(@PathVariable final Integer id) {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity < Employee > entity = new HttpEntity < > (httpHeaders);
    return restTemplate.exchange(URI_EMPLOYEE + id, HttpMethod.DELETE, entity, Employee.class);
}

5. 运行应用程序

我们可以在我们的应用类中的main方法中运行应用程序。然后我们可以使用Postman来测试端点。

6. 使用Spring Boot RestTemplate测试端点

让我们使用Spring RestTemplate来测试这些端点。


使用getForObject()获取所有雇员


使用getForObject()获取一个雇员的id:


使用 exchange()获取所有雇员


使用postForObject()创建雇员


使用delete()删除雇员

7. 使用RestTemplate配置超时

我们可以使用ClientHttpRequestFactory来配置RestTemplate,给连接添加一个超时。我们还可以使用HttpClient添加更多的配置。我们必须为此添加httpclient的依赖关系。

<dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
</dependency>

我们将创建另一个RestTemplate的Bean,并将ClinetHttpRequestFactory对象作为一个依赖关系传递。我们可以给ClientHttpRequestFactory Bean设定超时时间。

@Bean("restTemplateWithTimeout")
RestTemplate restTemplateWithTimeout() {
    return new RestTemplate(getClientHttpRequestFactory());
}

ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
        = new HttpComponentsClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(timeout);
    return clientHttpRequestFactory;
}

ClientHttpRequestFactory getClientHttpRequestFactoryV1() {
    int timeout = 5000;
    RequestConfig config = RequestConfig.custom()
        .setConnectTimeout(timeout)
        .setConnectionRequestTimeout(timeout)
        .setSocketTimeout(timeout)
        .build();
    CloseableHttpClient client = HttpClientBuilder
        .create()
        .setDefaultRequestConfig(config)
        .build();
    return new HttpComponentsClientHttpRequestFactory(client);
}

总结

在这篇文章中,我们创建了一个端到端的Spring Boot应用程序,并使用创建的Spring Boot休息模板控制器调用其端点。我们讨论了主要的HTTP方法,并使用RestTemplate来协调使用所有这些方法的请求。这篇文章的源码可以从GitHub repository中获得。

相关文章