レガシーREST クライアントの利用
|
このガイドは、Quarkus 2.8までデフォルトのJakarta REST(旧称JAX-RS)実装であった RESTEasy Classic と互換性のあるREST Clientに関するものです。 現在では、従来のブロック型ワークロードとリアクティブ型ワークロードを同様にサポートするRESTEasy Reactiveの使用が推奨されています。RESTEasy Reactiveの詳細については、 REST Client Reactiveガイド、サーバーサイドについては、 REST JSON入門ガイド、またはより詳細な RESTEasy Reactiveガイドを参照してください。 |
このガイドでは、MicroProfile REST Clientを使用して、ほとんど手間をかけずにREST APIとやりとりする方法を説明します。
| サーバーサイドで JSON REST API を書く必要がある場合は、JSON REST API ガイドを参照してください。 |
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOMEが適切に設定されていること -
Apache Maven 3.9.6
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。
Gitレポジトリをクローンするか git clone -b 3.8 https://github.com/quarkusio/quarkus-quickstarts.git 、 アーカイブ をダウンロードします。
ソリューションは resteasy-client-quickstart ディレクトリ にあります。
Mavenプロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-Dパラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=resteasy-client-quickstart"
このコマンドは、REST エンドポイントを持つ Maven プロジェクトを生成し、 rest-client と resteasy-jackson のエクステンションをインポートします。
-
REST サーバーのサポートのために
resteasyとresteasy-jacksonのエクステンションを使用しています。 -
resteasy-clientとresteasy-client-jacksonのエクステンションで REST クライアントをサポートしています。
Quarkusプロジェクトがすでに設定されている場合は、プロジェクトのベースディレクトリで次のコマンドを実行することで、 resteasy-client 、 resteasy-client-jackson エクステンションをプロジェクトに追加できます:
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 アノテーションです。
|
デフォルトで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 | この設定をすると、 ExtensionsService を使って実行されるすべてのリクエストは、ベースURLとして https://stage.code.quarkus.io をベース URL として使用することを意味します。
上記の設定を使うと、 io.quarkus:quarkus-resteasy-client の値で ExtensionsService の getById メソッドを呼び出すと、HTTP GET リクエストは次のようになります。 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メソッドに対してのみリダイレクトが行われるという重要な注意点があります。
非同期サポート
rest クライアントは非同期の rest 呼び出しをサポートしています。非同期のサポートには、 CompletionStage と Uni ( quarkus-resteasy-client-mutiny エクステンションモジュールが必要) の 2 種類があります。 ExtensionsService REST インターフェイスに getByIdAsync メソッドを追加してみましょう。コードは次のようになります:
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 で実行されます。
ネイティブ実行可能ファイルを ./mvnw clean package -Pnative で生成することもできます:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.package.type=native
REST クライアントと RESTEasy のやりとり
Quarkusでは、REST Clientエクステンションと RESTEasyエクステンション は、同じインフラストラクチャを共有しています。この考慮の重要な結果の1つは、(Jakarta RESTの意味での)プロバイダのリストを共有することです。
例えば、 WriterInterceptor を宣言した場合、デフォルトではサーバーの呼び出しとクライアントの呼び出しの両方をインターセプトしますが、これは望ましい動作ではないかもしれません。
しかし、このデフォルトの動作を変更してプロバイダを制約することができます。
-
プロバイダに
@ConstrainedTo(RuntimeType.CLIENT)アノテーションを追加することで、 クライアント コールのみを考慮します。 -
プロバイダに
@ConstrainedTo(RuntimeType.SERVER)アノテーションを追加することで、 サーバー コールのみを考慮します。
テストにモックHTTPサーバーを使用する
場合によっては、クライアント自体をモックするのではなく、リモートエンドポイント (HTTP サーバー) をモックしたい場合があります。これは、ネイティブテスト、またはプログラムで作成されたクライアントに特に役立つ場合があります。
You can easily mock an HTTP Server with Wiremock. The Wiremock section of the Quarkus - Using the REST Client describes how to set it up in detail.