消费 RESTful Web 服务

本指南将引导您完成创建一个消费 RESTful Web 服务的应用程序的过程。

您将构建什么

您将构建一个使用 Spring 的 RestTemplate 来在 https://:8080/api/random 获取随机 Spring Boot 引语的应用程序。

你需要什么

如何完成本指南

与大多数 Spring 入门指南一样,您可以从头开始并完成每个步骤,也可以跳过您已熟悉的基本设置步骤。无论哪种方式,您最终都会得到可工作的代码。

从头开始,请转到从 Spring Initializr 开始

跳过基础知识,请执行以下操作

完成后,您可以对照 gs-consuming-rest/complete 中的代码检查您的结果。

从 Spring Initializr 开始

您可以使用这个预初始化项目,然后点击“生成”下载一个 ZIP 文件。该项目已配置好以适应本教程中的示例。

手动初始化项目

  1. 导航到 https://start.spring.io。此服务会为您拉取应用程序所需的所有依赖项,并为您完成大部分设置。

  2. 选择 Gradle 或 Maven 以及您想要使用的语言。

  3. 点击 Dependencies 并选择 HTTP Client

  4. 单击生成

  5. 下载生成的 ZIP 文件,这是一个已根据您的选择配置好的 Web 应用程序存档。

如果您的 IDE 集成了 Spring Initializr,您可以从 IDE 中完成此过程。
您还可以从 GitHub fork 项目,并在您的 IDE 或其他编辑器中打开它。

获取 REST 资源

项目设置完成后,您可以创建一个简单的应用程序来消费 RESTful 服务。

在此之前,您需要一个 REST 资源的来源。我们提供了一个此类服务的示例,地址是 https://github.com/spring-guides/quoters。您可以在一个单独的终端中运行该应用程序,并在 https://:8080/api/random 访问结果。该地址会随机获取关于 Spring Boot 的引语,并以 JSON 文档的形式返回。其他有效地址包括 https://:8080/api/(所有引语)和 https://:8080/api/1(第一个引语)、https://:8080/api/2(第二个引语),依此类推(目前最多 10 个)。

如果您通过网络浏览器或 curl 请求该 URL,您会收到一个类似以下的 JSON 文档

{
   type: "success",
   value: {
      id: 10,
      quote: "Really loving Spring Boot, makes stand alone Spring apps easy."
   }
}

这足够简单,但是通过浏览器或 curl 获取时并没有太大用处。

通过编程方式消费 REST web 服务是更实用的方法。为了帮助您完成这项任务,Spring 提供了一个方便的模板类,名为 RestClientRestClient 使与大多数 RESTful 服务的交互成为一行咒语。它甚至可以将数据绑定到自定义域类型。

首先,您需要创建一个域类(一个 Java 记录类或 Kotlin 数据类)来包含所需的数据。以下清单显示了 Quote 类,您可以将其用作您的域类

Java
package com.example.consumingrest;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public record Quote(String type, Value value) { }
Kotlin
package com.example.consumingrest

import com.fasterxml.jackson.annotation.JsonIgnoreProperties

@JsonIgnoreProperties(ignoreUnknown = true)
data class Quote(
    val type: String,
    val value: Value
)

该域类使用 Jackson JSON 处理库的 @JsonIgnoreProperties 注解,表示应忽略此类型中未绑定的任何属性。

要将数据直接绑定到您的自定义类型,您需要指定变量名与从 API 返回的 JSON 文档中的键完全相同。如果您的变量名和 JSON 文档中的键不匹配,您可以使用 @JsonProperty 注解来指定 JSON 文档的确切键。(本例将每个变量名与一个 JSON 键匹配,因此您不需要在此处使用该注解。)

您还需要一个额外的域类来嵌入内部引语本身。Value 类满足了这一需求,如下面的清单所示

Java
package com.example.consumingrest;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public record Value(Long id, String quote) { }
Kotlin
package com.example.consumingrest

import com.fasterxml.jackson.annotation.JsonIgnoreProperties

@JsonIgnoreProperties(ignoreUnknown = true)
data class Value(
    val id: Long,
    val quote: String
)

这使用相同的注解,但映射到其他数据字段。

完成应用程序

Initializr 创建了一个带有 main() 方法的类,如下面的清单所示

Java
package com.example.consumingrest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ConsumingRestApplication {

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

}
Kotlin
package com.example.consumingrest

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class ConsumingRestApplication

fun main(args: Array<String>) {
    runApplication<ConsumingRestApplication>(*args)
}

现在您需要向 ConsumingRestApplication 类添加一些其他内容,以使其能够显示来自我们的 RESTful 源的引语。您需要添加

  • 一个日志记录器,用于将输出发送到日志(本例中是控制台)。

  • 一个 CommandLineRunner,它使用自动配置的 RestClient.Builder 构建一个 RestClient 实例,并使用它在启动时获取我们的引语。

以下清单显示了完成的 ConsumingRestApplication

Java
package com.example.consumingrest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.web.client.RestClient;

@SpringBootApplication
public class ConsumingRestApplication {

	private static final Logger log = LoggerFactory.getLogger(ConsumingRestApplication.class);

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

	@Bean
	@Profile("!test")
	public ApplicationRunner run(RestClient.Builder builder) {
		RestClient restClient = builder.baseUrl("https://:8080").build();
		return args -> {
			Quote quote = restClient
					.get().uri("/api/random")
					.retrieve()
					.body(Quote.class);
			log.info(quote.toString());
		};
	}
}
Kotlin
package com.example.consumingrest

import org.slf4j.LoggerFactory
import org.springframework.boot.ApplicationRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Profile
import org.springframework.web.client.RestClient
import org.springframework.web.client.toEntity

private val log = LoggerFactory.getLogger(ConsumingRestApplication::class.java)

@SpringBootApplication
class ConsumingRestApplication {

    @Bean
    @Profile("!test")
    fun run(builder: RestClient.Builder) = ApplicationRunner {
        val quote = builder.build().get().uri("https://:8080/api/random")
            .retrieve().toEntity<Quote>()
        log.info(quote.toString())
    }
}

fun main(args: Array<String>) {
    runApplication<ConsumingRestApplication>(*args)
}

运行应用程序

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必要依赖项、类和资源并运行的单个可执行 JAR 文件。构建可执行 JAR 使在整个开发生命周期中,跨不同环境等,轻松交付、版本化和部署服务作为应用程序。

如果您使用 Gradle,您可以通过使用 ./gradlew bootRun 运行应用程序。或者,您可以通过使用 ./gradlew build 构建 JAR 文件,然后按如下方式运行 JAR 文件

java -jar build/libs/gs-consuming-rest-0.1.0.jar

如果您使用 Maven,您可以通过使用 ./mvnw spring-boot:run 运行应用程序。或者,您可以使用 ./mvnw clean package 构建 JAR 文件,然后按如下方式运行 JAR 文件

java -jar target/gs-consuming-rest-0.1.0.jar
这里描述的步骤创建了一个可运行的 JAR。您还可以构建一个经典的 WAR 文件

您应该会看到类似以下内容的输出,但引语是随机的

2019-08-22 14:06:46.506  INFO 42940 --- [           main] c.e.c.ConsumingRestApplication           : Quote{type='success', value=Value{id=1, quote='Working with Spring Boot is like pair-programming with the Spring developers.'}}
如果您看到错误提示 Could not extract response: no suitable HttpMessageConverter found for response type [class com.example.consumingrest.Quote],则可能是您所处的环境无法连接到后端服务(如果能连接到,它会发送 JSON)。也许您位于公司代理后面。请尝试将 http.proxyHosthttp.proxyPort 系统属性设置为适合您环境的值。

总结

恭喜!您刚刚使用 Spring Boot 开发了一个简单的 REST 客户端。

获取代码