レガシーREST クライアントの利用
このガイドは、Quarkus 2.8までデフォルトのJakarta REST(旧称JAX-RS)実装であった RESTEasy Classic と互換性のあるREST Clientに関するものです。 It is now recommended to use Quarkus REST (formerly RESTEasy Reactive), which supports equally well traditional blocking workloads and reactive workloads. For more information about Quarkus REST, please see the REST Client guide and, for the server side, the introductory REST JSON guide or the more detailed Quarkus REST guide. |
このガイドでは、MicroProfile REST Clientを使用して、ほとんど手間をかけずにREST APIとやりとりする方法を説明します。
サーバーサイドで JSON REST API を書く必要がある場合は、JSON REST API ガイドを参照してください。 |
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.8
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。
Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.git
、 アーカイブ をダウンロードします。
The solution is located in the resteasy-client-quickstart
directory.
Mavenプロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=resteasy-client-quickstart"
このコマンドは、REST エンドポイントを持つ Maven プロジェクトを生成し、 rest-client
と resteasy-jackson
のエクステンションをインポートします。
-
REST サーバーのサポートのために
resteasy
とresteasy-jackson
のエクステンションを使用しています。 -
the
resteasy-client
andresteasy-client-jackson
extensions for the REST client support.
If you already have your Quarkus project configured, you can add the resteasy-client
and the resteasy-client-jackson
extensions
to your project by running the following command in your project base directory:
quarkus extension add resteasy-client,resteasy-client-jackson
./mvnw quarkus:add-extension -Dextensions='resteasy-client,resteasy-client-jackson'
./gradlew addExtension --extensions='resteasy-client,resteasy-client-jackson'
これにより、 pom.xml
に以下が追加されます:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client-jackson</artifactId>
</dependency>
implementation("io.quarkus:quarkus-resteasy-client")
implementation("io.quarkus:quarkus-resteasy-client-jackson")
モデルのセットアップ
このガイドでは、 stage.code.quarkus.io サービスが提供するREST APIの一部を利用する方法をデモします。まず最初に、 使用するモデルを`Extension` POJO の形式でセットアップします。
src/main/java/org/acme/rest/client/Extension.java
ファイルを作成し、以下の内容を設定します。
package org.acme.rest.client;
import java.util.List;
public class Extension {
public String id;
public String name;
public String shortName;
public List<String> keywords;
}
上記のモデルは、サービスによって提供されるフィールドのサブセットに過ぎませんが、このガイドの目的には十分です。
インターフェースの作成
RESTEasy REST Client の使用方法は、適切な Jakarta REST と MicroProfile アノテーションを使用してインターフェースを作成するだけです。この場合、インターフェースは src/main/java/org/acme/rest/client/ExtensionsService.java
で作成し、以下の内容を持つ必要があります:
package org.acme.rest.client;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;
@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {
@GET
Set<Extension> getById(@QueryParam String id);
}
getById
メソッドは、私たちのコードに、Code Quarkus API からidを指定してエクステンションを問い合わせる機能を与えます。クライアントがすべてのネットワーキングとマーシャリングを処理してくれるので、このような技術的な詳細は一切ありません。
上のコードのアノテーションの目的は以下の通りです。
-
@RegisterRestClient
により、Quarkusは、このインターフェイスがRESTクライアントとしてCDIインジェクションに利用可能であることを知ることができます。 -
@Path
と@GET
、@QueryParam
は、サービスへのアクセス方法を定義するために使用される標準的な Jakarta REST アノテーションです。
When a JSON extension is installed such as デフォルトでJSONを使用したくない場合は、 JSONのデフォルトに依存しない場合は、エンドポイントに |
パスパラメーター
GETリクエストにパスパラメータが必要な場合は、 @QueryParam
の代わりに(または追加で) @PathParam("parameter-name")
を利用することができます。パスとクエリパラメータは、必要に応じて組み合わせることができます(以下のモックの例を参照してください)。
package org.acme.rest.client;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.util.Set;
@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {
@GET
@Path("/stream/{stream}")
Set<Extension> getByStream(@PathParam String stream, @QueryParam("id") String id);
}
コンフィグレーションの作成
REST 呼び出しが行われるベース URL を決定するために、REST クライアントは application.properties
からの設定を使用します。プロパティーの名前は、以下のコードで表示される特定の規則に従う必要があります。
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.ExtensionsService".url=https://stage.code.quarkus.io/api # (1)
quarkus.rest-client."org.acme.rest.client.ExtensionsService".scope=jakarta.inject.Singleton # (2)
1 | Having this configuration means that all requests performed using ExtensionsService will use https://stage.code.quarkus.io as the base URL.
Using the configuration above, calling the getById method of ExtensionsService with a value of io.quarkus:quarkus-resteasy-client would result in an HTTP GET request being made to https://stage.code.quarkus.io/api/extensions?id=io.quarkus:quarkus-rest-client . |
2 | この設定をすると、 ExtensionsService のデフォルトのスコープは @Singleton になります。サポートされているスコープの値は @Singleton , @Dependent , @ApplicationScoped および @RequestScoped です。既定のスコープは @Dependent です。デフォルトのスコープはインターフェイス上で定義することもできます。 |
org.acme.rest.client.ExtensionsService
は、前のセクションで作成した`ExtensionsService` インターフェイスの完全修飾名と 一致しなければならない ことに注意してください。
標準のMicroProfile Rest Clientのプロパティ表記を使用して、クライアントを設定することもできます。
プロパティがQuarkus記法とMicroProfile記法の両方で指定されている場合、Quarkus記法が優先されます。 |
構成を容易にするために、 @RegisterRestClient
configKey
プロパティーを使用して、インターフェイスの完全修飾名とは別の構成ルートを使用することができます。
@RegisterRestClient(configKey="extensions-api")
public interface ExtensionsService {
[...]
}
# Your configuration properties
quarkus.rest-client.extensions-api.url=https://stage.code.quarkus.io/api
quarkus.rest-client.extensions-api.scope=jakarta.inject.Singleton
ホスト名の検証を無効にする
特定のRESTクライアントのSSLホスト名検証を無効にするには、次のプロパティーを構成に追加します。
quarkus.rest-client.extensions-api.verify-host=false
この設定は、SSLホスト名検証を無効にするため、本番運用では使用しないでください。 |
さらに、カスタムのホスト名検証ストラテジーを使用するようにRESTクライアントを設定することができます。必要なのは、インターフェース javax.net.ssl.HostnameVerifier
を実装したクラスを用意し、以下のプロパティを設定に追加することだけです。
quarkus.rest-client.extensions-api.hostname-verifier=<full qualified custom hostname verifier class name>
Quarkus REST クライアントは、 |
Jakarta RESTリソースの作成
src/main/java/org/acme/rest/client/ExtensionsResource.java
ファイルを以下の内容で作成してください:
package org.acme.rest.client;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.util.Set;
@Path("/extension")
public class ExtensionsResource {
@Inject
@RestClient
ExtensionsService extensionsService;
@GET
@Path("/id/{id}")
public Set<Extension> id(@PathParam String id) {
return extensionsService.getById(id);
}
}
標準の CDI @Inject
アノテーションに加えて、MicroProfile @RestClient
アノテーションを使用して ExtensionsService
を注入する必要があることに注意してください。
テストの更新
また、エンドポイントに加えられた変更を反映させるために、機能テストを更新する必要があります。 src/test/java/org/acme/rest/client/ExtensionsResourceTest.java
ファイルを編集し、 testExtensionIdEndpoint
メソッドの内容を変更します。
package org.acme.rest.client;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.greaterThan;
import org.acme.rest.client.resources.WireMockExtensionsResource;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
@QuarkusTestResource(WireMockExtensionsResource.class)
public class ExtensionsResourceTest {
@Test
public void testExtensionsIdEndpoint() {
given()
.when().get("/extension/id/io.quarkus:quarkus-rest-client")
.then()
.statusCode(200)
.body("$.size()", is(1),
"[0].id", is("io.quarkus:quarkus-rest-client"),
"[0].name", is("REST Client Classic"),
"[0].keywords.size()", greaterThan(1),
"[0].keywords", hasItem("rest-client"));
}
}
上のコードでは、 REST Assured の json-path 機能を使用しています。
リダイレクト
HTTPサーバーは、ステータスコードが "3 "で始まるレスポンスと、リダイレクト先のURLを保持するHTTPヘッダー "Location "を送信することで、レスポンスを別の場所にリダイレクトできます。RESTクライアントがHTTPサーバーからリダイレクトレスポンスを受信しても、新しい場所への再リクエストは自動的に実行されません。ただし、"follow-redirects "プロパティを有効にすることで、自動リダイレクトを有効にすることができます:
-
quarkus.rest-client.follow-redirects
で、すべてのRESTクライアントに対してリダイレクトが有効になります。 -
quarkus.rest-client.<client-prefix>.follow-redirects
で、特定のRESTクライアントに対するリダイレクトを有効にすることができます。
このプロパティがtrueの場合、REST Clientは、HTTPサーバーからリダイレクトレスポンスを受信すると、新しいリクエストを実行します。
さらに、プロパティ "max-redirects" を使って、リダイレクトの回数を制限することができます。
RFC2616 仕様によると、デフォルトではGETまたはHEADメソッドに対してのみリダイレクトが行われるという重要な注意点があります。
非同期サポート
The rest client supports asynchronous rest calls.
Async support comes in 2 flavors: you can return a CompletionStage
or a Uni
(requires the quarkus-resteasy-client-mutiny
extension).
Let’s see it in action by adding a getByIdAsync
method in our ExtensionsService
REST interface. The code should look like:
package org.acme.rest.client;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {
@GET
Set<Extension> getById(@QueryParam String id);
@GET
CompletionStage<Set<Extension>> getByIdAsync(@QueryParam String id);
}
src/main/java/org/acme/rest/client/ExtensionsResource.java
ファイルを開き、以下の内容で更新してください。
package org.acme.rest.client;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;
@Path("/extension")
public class ExtensionsResource {
@Inject
@RestClient
ExtensionsService extensionsService;
@GET
@Path("/id/{id}")
public Set<Extension> id(@PathParam String id) {
return extensionsService.getById(id);
}
@GET
@Path("/id-async/{id}")
public CompletionStage<Set<Extension>> idAsync(@PathParam String id) {
return extensionsService.getByIdAsync(id);
}
}
非同期メソッドをテストするには、ExtensionsResourceTest
に以下のテストメソッドを追加します。
@Test
public void testExtensionIdAsyncEndpoint() {
given()
.when().get("/extension/id-async/io.quarkus:quarkus-rest-client")
.then()
.statusCode(200)
.body("$.size()", is(1),
"[0].id", is("io.quarkus:quarkus-rest-client"),
"[0].name", is("REST Client Classic"),
"[0].keywords.size()", greaterThan(1),
"[0].keywords", hasItem("rest-client"));
}
Uni
版は非常に似ています。
package org.acme.rest.client;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
import io.smallrye.mutiny.Uni;
@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {
// ...
@GET
Uni<Set<Extension>> getByIdAsUni(@QueryParam String id);
}
ExtensionsResource
は次のようになります。
package org.acme.rest.client;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;
import io.smallrye.mutiny.Uni;
@Path("/extension")
public class ExtensionsResource {
@Inject
@RestClient
ExtensionsService extensionsService;
// ...
@GET
@Path("/id-uni/{id}")
public Uni<Set<Extension>> idMutiny(@PathParam String id) {
return extensionsService.getByIdAsUni(id);
}
}
Mutiny
前のスニペットでは、Mutinyのリアクティブ型を使用していますが、もし慣れていない場合は、まず Mutiny - 直感的なリアクティブプログラミングライブラリ を読んでください。 |
Uni
を返すとき、すべての サブスクリプション は 、リモートサービスを呼び出します。つまり、 Uni
で再購読してリクエストを再送信するか、以下のように retry
を使用することができます。
@Inject @RestClient ExtensionsService extensionsService;
// ...
extensionsService.getByIdAsUni(id)
.onFailure().retry().atMost(10);
CompletionStage
を使用する場合は、再試行のためにサービスのメソッドを呼び出す必要があります。この違いは、 Mutiny とそのサブスクリプションプロトコルの lazy (遅延) 性の側面から来ています。これについての詳細は Mutiny の ドキュメント を参照してください。
カスタムヘッダーのサポート
MicroProfile REST クライアントでは、 ClientHeadersFactory
を @RegisterClientHeaders
アノテーションで登録することで、リクエストヘッダを修正することができます。
ExtensionsResource
REST インタフェースに @RegisterClientHeaders
アノテーションを追加して、 RequestUUIDHeaderFactory
クラスを指すようにしてみましょう。
package org.acme.rest.client;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
import io.smallrye.mutiny.Uni;
@Path("/extensions")
@RegisterRestClient
@RegisterClientHeaders(RequestUUIDHeaderFactory.class)
public interface ExtensionsService {
@GET
Set<Extension> getById(@QueryParam String id);
@GET
CompletionStage<Set<Extension>> getByIdAsync(@QueryParam String id);
@GET
Uni<Set<Extension>> getByIdAsUni(@QueryParam String id);
}
そして、 RequestUUIDHeaderFactory
は次のようになります。
package org.acme.rest.client;
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.UUID;
@ApplicationScoped
public class RequestUUIDHeaderFactory implements ClientHeadersFactory {
@Override
public MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders, MultivaluedMap<String, String> clientOutgoingHeaders) {
MultivaluedMap<String, String> result = new MultivaluedHashMap<>();
result.add("X-request-uuid", UUID.randomUUID().toString());
return result;
}
}
上の例のように、 @Singleton
, @ApplicationScoped
などのスコープを定義するアノテーションを付けることで、 ClientHeadersFactory
の実装をCDI Beanにすることができます。
デフォルトのヘッダーファクトリー
また、カスタムファクトリーを指定せずに @RegisterClientHeaders
アノテーションを使用することもできます。その場合は、 DefaultClientHeadersFactoryImpl
ファクトリーが使用され、 org.eclipse.microprofile.rest.client.propagateHeaders
設定プロパティに記載されているすべてのヘッダーが修正されます。個々のヘッダー名はコンマで区切られています。
@Path("/extensions")
@RegisterRestClient
@RegisterClientHeaders
public interface ExtensionsService {
@GET
Set<Extension> getById(@QueryParam String id);
@GET
CompletionStage<Set<Extension>> getByIdAsync(@QueryParam String id);
@GET
Uni<Set<Extension>> getByIdAsUni(@QueryParam String id);
}
org.eclipse.microprofile.rest.client.propagateHeaders=Authorization,Proxy-Authorization
アプリケーションをパッケージ化して実行する
アプリケーションを実行します:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
REST Client extensionに関する基本情報を含む JSON オブジェクトが表示されます。
いつものように、アプリケーションは以下の方法でパッケージ化されます。
quarkus build
./mvnw install
./gradlew build
そして、java -jar target/quarkus-app/quarkus-run.jar
で実行されます。
次のようにネイティブ実行可能ファイルを生成することもできます。
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
REST クライアントと RESTEasy のやりとり
Quarkusでは、REST Clientエクステンションと RESTEasyエクステンション は、同じインフラストラクチャを共有しています。この考慮の重要な結果の1つは、(Jakarta RESTの意味での)プロバイダのリストを共有することです。
例えば、 WriterInterceptor
を宣言した場合、デフォルトではサーバーの呼び出しとクライアントの呼び出しの両方をインターセプトしますが、これは望ましい動作ではないかもしれません。
しかし、このデフォルトの動作を変更してプロバイダを制約することができます。
-
プロバイダに
@ConstrainedTo(RuntimeType.CLIENT)
アノテーションを追加することで、 クライアント コールのみを考慮します。 -
プロバイダに
@ConstrainedTo(RuntimeType.SERVER)
アノテーションを追加することで、 サーバー コールのみを考慮します。
テストにモックHTTPサーバーを使用する
場合によっては、クライアント自体をモックするのではなく、リモートエンドポイント (HTTP サーバー) をモックしたい場合があります。これは、ネイティブテスト、またはプログラムで作成されたクライアントに特に役立つ場合があります。
Wiremock を使用して HTTP サーバーを簡単にモックできます。Wiremock section of the Quarkus - Using the REST Client では、セットアップ方法について詳しく説明しています。