The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

マルチパートでの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.

RESTEasyは、 multipart/*multipart/form-data のマイムタイプを豊富にサポートしています。マルチパートマイムフォーマットは、コンテンツボディのリストを渡すために使用されます。 multipart/form-data は、Web アプリケーションのHTMLフォームドキュメントでよく見られ、一般的にはファイルのアップロードに使用されます。form-dataフォーマットは、他のマルチパートフォーマットと同じですが、インラインの各コンテンツには名前が関連付けられています。

このガイドでは、マルチパートでRESTEasy RESTクライアントを使用して、 multipart/form-data コンテンツタイプを必要とするREST APIとわずかな労力でやりとりする方法を説明します。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.6

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

ソリューション

次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

The solution is located in the resteasy-client-multipart-quickstart directory.

Mavenプロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。

コマンドラインインタフェース
quarkus create app org.acme:resteasy-client-multipart-quickstart \
    --extension='resteasy-client,resteasy,resteasy-multipart' \
    --no-code
cd resteasy-client-multipart-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.9.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=resteasy-client-multipart-quickstart \
    -Dextensions='resteasy-client,resteasy,resteasy-multipart' \
    -DnoCode
cd resteasy-client-multipart-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=resteasy-client-multipart-quickstart"

This command generates a Maven project with a REST endpoint and imports the resteasy-client and resteasy extensions. It also adds the resteasy-multipart extension to support multipart/form-data requests.

Quarkusプロジェクトがすでに設定されている場合は、プロジェクトのベースディレクトリで次のコマンドを実行することで、 resteasy-multipart エクステンションをプロジェクトに追加できます:

コマンドラインインタフェース
quarkus extension add resteasy-multipart
Maven
./mvnw quarkus:add-extension -Dextensions='resteasy-multipart'
Gradle
./gradlew addExtension --extensions='resteasy-multipart'

これにより、 pom.xml に以下が追加されます:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-multipart</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-resteasy-multipart")

モデルのセットアップ

このガイドでは、 multipart/form-data の入力を受け付けるRESTサービスを起動する方法を紹介します。リクエストが送信される前にペイロードが既知であることを想定しているので、POJOとしてモデル化することができます。

ペイロードが不明な場合は、代わりにRESTEasy のカスタムAPIを使用することもできます。その場合は、ガイドの最後にある RESTEasy Multipart Providers のリンクを参照してください。

まず最初に、 multipart/form-data のペイロードを定義するために使用するモデルを、 MultipartBody POJO の形で設定します。

src/main/java/org/acme/rest/client/multipart/MultipartBody.java ファイルを作成し、以下の内容を設定します。

package org.acme.rest.client.multipart;

import java.io.InputStream;

import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.core.MediaType;

import org.jboss.resteasy.annotations.providers.multipart.PartType;

public class MultipartBody {

    @FormParam("file")
    @PartType(MediaType.APPLICATION_OCTET_STREAM)
    public InputStream file;

    @FormParam("fileName")
    @PartType(MediaType.TEXT_PLAIN)
    public String fileName;
}

上のコードのアノテーションの目的は以下の通りです。

  • @FormParam は、リクエストエンティティ本体に含まれるフォームパラメータを定義するために使用される Jakarta REST の標準アノテーションです。

  • @PartType は、クライアントがマルチパートのリクエストを実行し、パートのコンテンツタイプを定義する際に必要となる、RESTEasy特有のアノテーションです。

インターフェースの作成

RESTEasy REST Client の使用方法は、適切な Jakarta REST と MicroProfile アノテーションを使用してインターフェースを作成するだけです。この場合、インターフェースは src/main/java/org/acme/rest/client/multipart/MultipartService.java で作成し、以下の内容を持つ必要があります:

package org.acme.rest.client.multipart;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;

@Path("/echo")
@RegisterRestClient
public interface MultipartService {

    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    String sendMultipartData(@MultipartForm MultipartBody data);

}

sendMultipartData メソッドは、コードに multipart/form-data リクエストをEchoサービスに POST する機能を与えます(デモ用に同じサーバーで実行しています)。このデモでは、 multipart/form-data パケットの正確に把握しているので、 @org.jboss.resteasy.annotations.providers.multipart.MultipartForm アノテーションを使用して、前のセクションで作成したモデルクラスにマッピングすることができます。

ネットワーキングやマーシャリングはすべてクライアントが行うので、私たちのコードにはそのような技術的な詳細は含まれません。

上のコードのアノテーションの目的は以下の通りです。

  • @RegisterRestClient により、Quarkusは、このインターフェイスがRESTクライアントとしてCDIインジェクションに利用可能であることを知ることができます。

  • @Path@POST は、サービスへのアクセス方法を定義するために使用される、標準的な Jakarta REST アノテーションです。

  • @MultipartForm は、multipart/form-dataマイムタイプの受信/送信リクエスト/レスポンスの値オブジェクトとして、パラメータを定義しています。

  • @Consumes は、このリクエストで消費されると予想されるコンテンツタイプを定義します(パラメータ)。

  • @Produces は、このリクエストによって生成されると予想されるコンテンツタイプを定義します(リターンタイプ)。

@Consumes@Produces は自動ネゴシエーションがサポートされているためオプションですが、予想されるコンテンツタイプを正確に定義するため、エンドポイントにアノテーションを付けることを強く推奨します。

これにより、ネイティブ実行可能ファイルに含まれるJakarta RESTプロバイダー(コンバーターとも言える)の数を絞ることができるようになります。

コンフィグレーションの作成

REST 呼び出しが行われるベース URL を決定するために、REST クライアントは application.properties からの設定を使用します。プロパティーの名前は、以下のコードで表示される特定の規則に従う必要があります。

# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=http://localhost:8080/

この設定を行うと、 org.acme.rest.client.multipart.MultipartService を使用して実行されるすべてのリクエストは、 http://localhost:8080/ をベースURLとします。

なお、org.acme.rest.client.multipart.MultipartService は、前のセクションで作成した MultipartService インターフェースの完全修飾名と一致していなければならないことに注意してください。

Jakarta RESTリソースの作成

src/main/java/org/acme/rest/client/multipart/MultipartClientResource.java のファイルを、以下の内容で作成します。

package org.acme.rest.client.multipart;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

import jakarta.inject.Inject;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import org.eclipse.microprofile.rest.client.inject.RestClient;

@Path("/client")
public class MultipartClientResource {

    @Inject
    @RestClient
    MultipartService service;

    @POST
    @Path("/multipart")
    @Produces(MediaType.TEXT_PLAIN)
    public String sendFile() throws Exception {
        MultipartBody body = new MultipartBody();
        body.fileName = "greeting.txt";
        body.file = new ByteArrayInputStream("HELLO WORLD".getBytes(StandardCharsets.UTF_8));
        return service.sendMultipartData(body);
    }
}

なお、標準のCDI @Inject アノテーションに加えて、マイクロプロファイル @RestClient アノテーションを使って MultipartService を入れる必要があることに注意してください。

サーバーの作成

デモ用に、サーバー部分として機能するシンプルなEchoエンドポイントを作成してみましょう。

src/main/java/org/acme/rest/client/multipart/server ディレクトリを作成し、以下の内容の EchoService.java ファイルを入れます。

package org.acme.rest.client.multipart.server;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/echo")
public class EchoService {

    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    public String echo(String requestBody) throws Exception {
        return requestBody;
    }
}

これは単にリクエストボディを返すだけなので、テスト目的として便利です。

テストの更新

また、エンドポイントに加えられた変更を反映させるために、機能テストを更新する必要があります。 src/test/java/org/acme/rest/client/multipart/MultipartClientResourceTest.java ファイルを下記のように編集します。

package org.acme.rest.client.multipart;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;

@QuarkusTest
public class MultipartClientResourceTest {

    @Test
    public void testMultipartDataIsSent() {
        given()
                .when().post("/client/multipart")
                .then()
                .statusCode(200)
                .body( containsString("Content-Disposition: form-data; name=\"file\""),
                        containsString("HELLO WORLD"),
                        containsString("Content-Disposition: form-data; name=\"fileName\""),
                        containsString("greeting.txt"));
    }

}

上記のコードでは、 REST Assuredを使用して、エコーサービスから返されるコンテンツにマルチパート要素が含まれていることをアサートしています。

テストは別のポートで実行されるため、 src/test/resources に、次に示す内容で application.properties を含める必要があります。

# Your configuration properties
quarkus.rest-client."org.acme.rest.client.multipart.MultipartService".url=http://localhost:8081/

アプリケーションをパッケージ化して実行する

アプリケーションを実行します:

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

ターミナルで、 curl -X POST http://localhost:8080/client/multipart を実行します。

以下のような出力が表示されるはずです。

--89d288bd-960f-460c-b266-64c5b4d170fa
Content-Disposition: form-data; name="fileName"
Content-Type: text/plain

greeting.txt
--89d288bd-960f-460c-b266-64c5b4d170fa
Content-Disposition: form-data; name="file"
Content-Type: application/octet-stream

HELLO WORLD
--89d288bd-960f-460c-b266-64c5b4d170fa--

いつものように、アプリケーションは以下の方法でパッケージ化されます。

コマンドラインインタフェース
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

そして、java -jar target/quarkus-app/quarkus-run.jar で実行されます。

次のようにネイティブ実行可能ファイルを生成することもできます。

コマンドラインインタフェース
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native

関連コンテンツ