REST サービスの呼び出し

Spring Boot は、リモート REST サービスを呼び出すためのさまざまな便利な方法を提供します。ノンブロッキングのリアクティブアプリケーションを開発していて、Spring WebFlux を使用している場合は、WebClient を使用できます。API をブロックしたい場合は、RestClient または RestTemplate を使用できます。

WebClient

クラスパスに Spring WebFlux がある場合は、WebClient を使用して リモート REST サービスを呼び出すことをお勧めします。WebClient インターフェースは関数型 API を提供し、完全にリアクティブです。WebClient の詳細については、Spring Framework ドキュメントの専用セクションを参照してください。

リアクティブ Spring WebFlux アプリケーションを作成していない場合は、WebClient の代わりに RestClient を使用できます。これは同様の機能の API を提供しますが、リアクティブではなくブロッキングです。

Spring Boot は、プロトタイプ WebClient.Builder Bean を作成し、事前構成します。これをコンポーネントに挿入し、それを使用して WebClient インスタンスを作成することを強くお勧めします。Spring Boot は、HTTP リソースを共有し、サーバーのものと同じ方法でコーデックのセットアップを反映するようにそのビルダーを構成しています (WebFlux HTTP コーデックの自動構成を参照)。

次のコードは典型的な例を示しています。

  • Java

  • Kotlin

import reactor.core.publisher.Mono;

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class MyService {

	private final WebClient webClient;

	public MyService(WebClient.Builder webClientBuilder) {
		this.webClient = webClientBuilder.baseUrl("https://example.org").build();
	}

	public Mono<Details> someRestCall(String name) {
		return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
	}

}
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono

@Service
class MyService(webClientBuilder: WebClient.Builder) {

	private val webClient: WebClient

	init {
		webClient = webClientBuilder.baseUrl("https://example.org").build()
	}

	fun someRestCall(name: String?): Mono<Details> {
		return webClient.get().uri("/{name}/details", name)
				.retrieve().bodyToMono(Details::class.java)
	}

}

WebClient ランタイム

Spring Boot は、アプリケーションのクラスパスで利用可能なライブラリに応じて、WebClient の駆動にどの ClientHttpConnector を使用するかを自動検出します。優先順位に従って、次のクライアントがサポートされます。

  1. Reactor Netty

  2. Jetty RS クライアント

  3. Apache HttpClient

  4. JDK HttpClient

クラスパス上で複数のクライアントが使用可能な場合は、最も優先されるクライアントが使用されます。

spring-boot-starter-webflux スターターは、デフォルトで io.projectreactor.netty:reactor-netty に依存しており、サーバーとクライアントの両方の実装をもたらします。代わりにリアクティブサーバーとして Jetty を使用することを選択した場合は、Jetty リアクティブ HTTP クライアントライブラリ org.eclipse.jetty:jetty-reactive-httpclient への依存関係を追加する必要があります。サーバーとクライアントに同じテクノロジーを使用すると、クライアントとサーバー間で HTTP リソースが自動的に共有されるため、利点があります。

開発者は、カスタム ReactorResourceFactory または JettyResourceFactory Bean を提供することにより、Jetty および Reactor Netty のリソース構成をオーバーライドできます。これは、クライアントとサーバーの両方に適用されます。

クライアントのその選択をオーバーライドする場合は、独自の ClientHttpConnector Bean を定義し、クライアント構成を完全に制御できます。

WebClient のカスタマイズ

WebClient カスタマイズには、カスタマイズの適用範囲に応じて 3 つの主なアプローチがあります。

カスタマイズの範囲をできる限り狭くするには、自動構成された WebClient.Builder を挿入し、必要に応じてそのメソッドを呼び出します。WebClient.Builder インスタンスはステートフルです: ビルダーでの変更は、その後作成されたすべてのクライアントに反映されます。同じビルダーで複数のクライアントを作成する場合は、WebClient.Builder other = builder.clone(); でビルダーを複製することも検討できます。

アプリケーション全体で、すべての WebClient.Builder インスタンスに追加のカスタマイズを行うには、WebClientCustomizer Bean を宣言し、注入の時点で WebClient.Builder をローカルに変更します。

最後に、元の API にフォールバックして WebClient.create() を使用できます。その場合、自動構成または WebClientCustomizer は適用されません。

WebClient SSL サポート

WebClient で使用される ClientHttpConnector でカスタム SSL 構成が必要な場合は、ビルダーの apply メソッドで使用できる WebClientSsl インスタンスを挿入できます。

WebClientSsl インターフェースは、application.properties または application.yaml ファイルで定義した任意の SSL バンドルへのアクセスを提供します。

次のコードは典型的な例を示しています。

  • Java

  • Kotlin

import reactor.core.publisher.Mono;

import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class MyService {

	private final WebClient webClient;

	public MyService(WebClient.Builder webClientBuilder, WebClientSsl ssl) {
		this.webClient = webClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
	}

	public Mono<Details> someRestCall(String name) {
		return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
	}

}
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono

@Service
class MyService(webClientBuilder: WebClient.Builder, ssl: WebClientSsl) {

	private val webClient: WebClient

	init {
		webClient = webClientBuilder.baseUrl("https://example.org")
				.apply(ssl.fromBundle("mybundle")).build()
	}

	fun someRestCall(name: String?): Mono<Details> {
		return webClient.get().uri("/{name}/details", name)
				.retrieve().bodyToMono(Details::class.java)
	}

}

RestClient

アプリケーションで Spring WebFlux またはプロジェクト Reactor を使用していない場合は、RestClient を使用して リモート REST サービスを呼び出すことをお勧めします。

RestClient インターフェースは、関数型スタイルのブロッキング API を提供します。

Spring Boot は、プロトタイプ RestClient.Builder Bean を作成し、事前構成します。これをコンポーネントに挿入し、それを使用して RestClient インスタンスを作成することを強くお勧めします。Spring Boot は、そのビルダーを HttpMessageConverters および適切な ClientHttpRequestFactory で構成しています。

次のコードは典型的な例を示しています。

  • Java

  • Kotlin

import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class MyService {

	private final RestClient restClient;

	public MyService(RestClient.Builder restClientBuilder) {
		this.restClient = restClientBuilder.baseUrl("https://example.org").build();
	}

	public Details someRestCall(String name) {
		return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
	}

}
import org.springframework.boot.docs.io.restclient.restclient.ssl.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient

@Service
class MyService(restClientBuilder: RestClient.Builder) {

	private val restClient: RestClient

	init {
		restClient = restClientBuilder.baseUrl("https://example.org").build()
	}

	fun someRestCall(name: String?): Details {
		return restClient.get().uri("/{name}/details", name)
				.retrieve().body(Details::class.java)!!
	}

}

RestClient のカスタマイズ

RestClient カスタマイズには、カスタマイズの適用範囲に応じて 3 つの主なアプローチがあります。

カスタマイズの範囲をできる限り狭くするには、自動構成された RestClient.Builder を挿入し、必要に応じてそのメソッドを呼び出します。RestClient.Builder インスタンスはステートフルです: ビルダーでの変更は、その後作成されたすべてのクライアントに反映されます。同じビルダーで複数のクライアントを作成する場合は、RestClient.Builder other = builder.clone(); でビルダーを複製することも検討できます。

アプリケーション全体で、すべての RestClient.Builder インスタンスに追加のカスタマイズを行うには、RestClientCustomizer Bean を宣言し、注入の時点で RestClient.Builder をローカルに変更します。

最後に、元の API にフォールバックして RestClient.create() を使用できます。その場合、自動構成または RestClientCustomizer は適用されません。

RestClient SSL サポート

RestClient で使用される ClientHttpRequestFactory でカスタム SSL 構成が必要な場合は、ビルダーの apply メソッドで使用できる RestClientSsl インスタンスを挿入できます。

RestClientSsl インターフェースは、application.properties または application.yaml ファイルで定義した任意の SSL バンドルへのアクセスを提供します。

次のコードは典型的な例を示しています。

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class MyService {

	private final RestClient restClient;

	public MyService(RestClient.Builder restClientBuilder, RestClientSsl ssl) {
		this.restClient = restClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
	}

	public Details someRestCall(String name) {
		return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
	}

}
import org.springframework.boot.autoconfigure.web.client.RestClientSsl
import org.springframework.boot.docs.io.restclient.restclient.ssl.settings.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient

@Service
class MyService(restClientBuilder: RestClient.Builder, ssl: RestClientSsl) {

	private val restClient: RestClient

	init {
		restClient = restClientBuilder.baseUrl("https://example.org")
				.apply(ssl.fromBundle("mybundle")).build()
	}

	fun someRestCall(name: String?): Details {
		return restClient.get().uri("/{name}/details", name)
				.retrieve().body(Details::class.java)!!
	}

}

SSL バンドルに加えて他のカスタマイズを適用する必要がある場合は、ClientHttpRequestFactorySettings クラスを ClientHttpRequestFactories とともに使用できます。

  • Java

  • Kotlin

import java.time.Duration;

import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class MyService {

	private final RestClient restClient;

	public MyService(RestClient.Builder restClientBuilder, SslBundles sslBundles) {
		ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS
			.withReadTimeout(Duration.ofMinutes(2))
			.withSslBundle(sslBundles.getBundle("mybundle"));
		ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings);
		this.restClient = restClientBuilder.baseUrl("https://example.org").requestFactory(requestFactory).build();
	}

	public Details someRestCall(String name) {
		return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
	}

}
import org.springframework.boot.ssl.SslBundles
import org.springframework.boot.web.client.ClientHttpRequestFactories
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
import java.time.Duration

@Service
class MyService(restClientBuilder: RestClient.Builder, sslBundles: SslBundles) {

	private val restClient: RestClient

	init {
		val settings = ClientHttpRequestFactorySettings.DEFAULTS
				.withReadTimeout(Duration.ofMinutes(2))
				.withSslBundle(sslBundles.getBundle("mybundle"))
		val requestFactory = ClientHttpRequestFactories.get(settings)
		restClient = restClientBuilder
				.baseUrl("https://example.org")
				.requestFactory(requestFactory).build()
	}

	fun someRestCall(name: String?): Details {
		return restClient.get().uri("/{name}/details", name).retrieve().body(Details::class.java)!!
	}

}

RestTemplate

Spring Framework の RestTemplate (Javadoc) クラスは RestClient よりも前から存在し、多くのアプリケーションが リモート REST サービスを呼び出すために使用する典型的な方法です。RestClient に移行したくない既存のコードがある場合、または RestTemplate API にすでに慣れている場合は、RestTemplate の使用を選択することもできます。

RestTemplate インスタンスは使用前にカスタマイズする必要があることが多いため、Spring Boot には単一の自動構成 RestTemplate Bean が提供されていません。ただし、RestTemplateBuilder は自動構成され、必要に応じて RestTemplate インスタンスの作成に使用できます。自動構成された RestTemplateBuilder により、適切な HttpMessageConverters と適切な ClientHttpRequestFactory が RestTemplate インスタンスに適用されます。

次のコードは典型的な例を示しています。

  • Java

  • Kotlin

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MyService {

	private final RestTemplate restTemplate;

	public MyService(RestTemplateBuilder restTemplateBuilder) {
		this.restTemplate = restTemplateBuilder.build();
	}

	public Details someRestCall(String name) {
		return this.restTemplate.getForObject("/{name}/details", Details.class, name);
	}

}
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate

@Service
class MyService(restTemplateBuilder: RestTemplateBuilder) {

	private val restTemplate: RestTemplate

	init {
		restTemplate = restTemplateBuilder.build()
	}

	fun someRestCall(name: String): Details {
		return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
	}

}

RestTemplateBuilder には、RestTemplate を迅速に構成するために使用できる便利なメソッドが多数含まれています。例: BASIC 認証サポートを追加するには、builder.basicAuthentication("user", "password").build() を使用できます。

RestTemplate のカスタマイズ

RestTemplate カスタマイズには、カスタマイズの適用範囲に応じて 3 つの主なアプローチがあります。

カスタマイズの範囲をできる限り狭くするには、自動構成された RestTemplateBuilder を挿入し、必要に応じてそのメソッドを呼び出します。各メソッド呼び出しは新しい RestTemplateBuilder インスタンスを返すため、カスタマイズはこのビルダーの使用にのみ影響します。

アプリケーション全体で追加のカスタマイズを行うには、RestTemplateCustomizer Bean を使用します。このような Bean はすべて、自動構成された RestTemplateBuilder に自動的に登録され、それを使用して作成されたすべてのテンプレートに適用されます。

次の例は、192.168.0.5 を除くすべてのホストに対してプロキシの使用を構成するカスタマイザーを示しています。

  • Java

  • Kotlin

import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.protocol.HttpContext;

import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class MyRestTemplateCustomizer implements RestTemplateCustomizer {

	@Override
	public void customize(RestTemplate restTemplate) {
		HttpRoutePlanner routePlanner = new CustomRoutePlanner(new HttpHost("proxy.example.com"));
		HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build();
		restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
	}

	static class CustomRoutePlanner extends DefaultProxyRoutePlanner {

		CustomRoutePlanner(HttpHost proxy) {
			super(proxy);
		}

		@Override
		protected HttpHost determineProxy(HttpHost target, HttpContext context) throws HttpException {
			if (target.getHostName().equals("192.168.0.5")) {
				return null;
			}
			return super.determineProxy(target, context);
		}

	}

}
import org.apache.hc.client5.http.classic.HttpClient
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner
import org.apache.hc.client5.http.routing.HttpRoutePlanner
import org.apache.hc.core5.http.HttpException
import org.apache.hc.core5.http.HttpHost
import org.apache.hc.core5.http.protocol.HttpContext
import org.springframework.boot.web.client.RestTemplateCustomizer
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.web.client.RestTemplate

class MyRestTemplateCustomizer : RestTemplateCustomizer {

	override fun customize(restTemplate: RestTemplate) {
		val routePlanner: HttpRoutePlanner = CustomRoutePlanner(HttpHost("proxy.example.com"))
		val httpClient: HttpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build()
		restTemplate.requestFactory = HttpComponentsClientHttpRequestFactory(httpClient)
	}

	internal class CustomRoutePlanner(proxy: HttpHost?) : DefaultProxyRoutePlanner(proxy) {

		@Throws(HttpException::class)
		public override fun determineProxy(target: HttpHost, context: HttpContext): HttpHost? {
			if (target.hostName == "192.168.0.5") {
				return null
			}
			return  super.determineProxy(target, context)
		}

	}

}

最後に、独自の RestTemplateBuilder Bean を定義できます。そうすることで、自動構成されたビルダーが置き換えられます。自動構成の場合と同様に、RestTemplateCustomizer Bean をカスタムビルダーに適用する場合は、RestTemplateBuilderConfigurer を使用して構成します。次の例では、カスタム接続と読み取りのタイムアウトも指定されていることを除いて、Spring Boot の自動構成が実行したものと一致する RestTemplateBuilder を公開しています。

  • Java

  • Kotlin

import java.time.Duration;

import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyRestTemplateBuilderConfiguration {

	@Bean
	public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
		return configurer.configure(new RestTemplateBuilder())
			.setConnectTimeout(Duration.ofSeconds(5))
			.setReadTimeout(Duration.ofSeconds(2));
	}

}
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyRestTemplateBuilderConfiguration {

	@Bean
	fun restTemplateBuilder(configurer: RestTemplateBuilderConfigurer): RestTemplateBuilder {
		return configurer.configure(RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5))
			.setReadTimeout(Duration.ofSeconds(2))
	}

}

最も極端な(そしてめったに使用されない)オプションは、コンフィギュレーターを使用せずに独自の RestTemplateBuilder Bean を作成することです。自動構成されたビルダーを置き換えるだけでなく、これにより RestTemplateCustomizer Bean が使用されなくなります。

RestTemplate SSL サポート

RestTemplate でカスタム SSL 構成が必要な場合は、次の例に示すように SSL バンドルを RestTemplateBuilder に適用できます。

  • Java

  • Kotlin

import org.springframework.boot.docs.io.restclient.resttemplate.Details;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MyService {

	private final RestTemplate restTemplate;

	public MyService(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
		this.restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build();
	}

	public Details someRestCall(String name) {
		return this.restTemplate.getForObject("/{name}/details", Details.class, name);
	}

}
import org.springframework.boot.docs.io.restclient.resttemplate.Details
import org.springframework.boot.ssl.SslBundles
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate

@Service
class MyService(restTemplateBuilder: RestTemplateBuilder, sslBundles: SslBundles) {

    private val restTemplate: RestTemplate

    init {
        restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build()
    }

    fun someRestCall(name: String): Details {
        return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
    }

}

RestClient および RestTemplate の HTTP クライアントの検出

Spring Boot は、アプリケーションのクラスパスで使用可能なライブラリに応じて、RestClient および RestTemplate で使用する HTTP クライアントを自動検出します。優先順位に従って、次のクライアントがサポートされます。

  1. Apache HttpClient

  2. Jetty HttpClient

  3. OkHttp (非推奨)

  4. シンプルな JDK クライアント (HttpURLConnection)

クラスパス上で複数のクライアントが使用可能な場合は、最も優先されるクライアントが使用されます。