The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

RESTEasy Classic

このガイドは、Quarkus 2.8 までデフォルトの Jakarta REST (旧称 JAX-RS) 実装であった RESTEasy Classic に関するものです。

現在は、従来のブロッキングワークロードとリアクティブワークロードの両方を同様にサポートする Quarkus REST (旧称 RESTEasy Reactive) の使用が推奨されています。

Quarkus REST の詳細は、REST JSON 入門ガイド または Quarkus REST リファレンスドキュメント を参照してください。

RESTEasy Classic をベースにした REST クライアント (JSON のサポートを含む) が必要な場合は、別のガイド を参照してください。

アーキテクチャ

このガイドで作成されるアプリケーションは簡単です。ユーザーがフォームを通じてリストに要素を追加でき、それに応じてリストが更新されます。

ブラウザとサーバー間の情報はすべてJSON形式になっています。

Maven プロジェクトの作成

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

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

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

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

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.19.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rest-json-quickstart \
    -Dextensions='resteasy-jackson' \
    -DnoCode
cd rest-json-quickstart

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

Windowsユーザーの場合:

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

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

このコマンドは、RESTEasy/Jakarta REST および Jackson エクステンションをインポートする新しいプロジェクトを生成し、特に次の依存関係を追加します。

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

ユーザーエクスペリエンスを向上させるために、Quarkus は 3 つの Jackson Java 8 モジュール を登録しているので、手動で登録する必要はありません。

Quarkus は JSON-B もサポートしているため、Jackson よりも JSON-B を利用したい場合は、代わりに RESTEasy JSON-B エクステンションを依存関係に持つプロジェクトを作成することができます。

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

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

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

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.19.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rest-json-quickstart \
    -Dextensions='resteasy-jsonb' \
    -DnoCode
cd rest-json-quickstart

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

Windowsユーザーの場合:

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

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

このコマンドは、RESTEasy/Jakarta REST および JSON-B エクステンションをインポートする新しいプロジェクトを生成し、特に次の依存関係を追加します。

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

初めての JSON REST サービスの作成

この例では、果物のリストを管理するアプリケーションを作成します。

まず、次のように Fruit Bean を作成してみましょう。

package org.acme.rest.json;

public class Fruit {

    public String name;
    public String description;

    public Fruit() {
    }

    public Fruit(String name, String description) {
        this.name = name;
        this.description = description;
    }
}

派手なことは何もありません。注意すべき重要なことはJSONシリアライズレイヤーがデフォルトコンストラクターを必要とすることだけです。

次に、 org.acme.rest.json.FruitResource クラスを以下のように作成します。

package org.acme.rest.json;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Set;

import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;

@Path("/fruits")
public class FruitResource {

    private Set<Fruit> fruits = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));

    public FruitResource() {
        fruits.add(new Fruit("Apple", "Winter fruit"));
        fruits.add(new Fruit("Pineapple", "Tropical fruit"));
    }

    @GET
    public Set<Fruit> list() {
        return fruits;
    }

    @POST
    public Set<Fruit> add(Fruit fruit) {
        fruits.add(fruit);
        return fruits;
    }

    @DELETE
    public Set<Fruit> delete(Fruit fruit) {
        fruits.removeIf(existingFruit -> existingFruit.name.contentEquals(fruit.name));
        return fruits;
    }
}

実装は非常に簡単で、Jakarta RESTアノテーションを使用してエンドポイントを定義するだけです。

Fruit オブジェクトは、プロジェクトの初期化時に選択したエクステンションに応じて、 JSON-B または Jackson によって自動的にシリアライズ/デシリアライズされます。

quarkus-resteasy-jacksonquarkus-resteasy-jsonb などの JSON エクステンションがインストールされている場合、Quarkus はほとんどの戻り値に対して application/json メディアタイプをデフォルトとして使用します。 これは、String (デフォルトは text/plain) や File (デフォルトは application/octet-stream) などの特定のよく知られたタイプを除き、@Produces または @Consumes アノテーションを使用してオーバーライドできます。

デフォルトの JSON 動作を無効にするには、quarkus.resteasy-json.default-json=false を設定します。デフォルトが自動ネゴシエーションに戻ります。 この場合、JSON を使用するには、エンドポイントに @Produces(MediaType.APPLICATION_JSON) および @Consumes(MediaType.APPLICATION_JSON) アノテーションを含める必要があります。

JSON のデフォルトに依存しない場合は、エンドポイントで @Produces および @Consumes アノテーションを使用して、予想されるコンテンツタイプを正確に指定することを強く推奨します。 これにより、ネイティブ実行可能ファイルに含まれる Jakarta REST プロバイダー (基本的にはコンバーター) の数を減らすことができます。

JSON サポートの設定

Jackson

Quarkus では、CDI 経由で取得された (Quarkus エクステンションによって利用される) デフォルトの Jackson ObjectMapper は、(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES を無効にして) 不明なプロパティーを無視するように設定されています。

Jackson のデフォルトの動作に戻すには、application.propertiesquarkus.jackson.fail-on-unknown-properties=true を設定するか、@JsonIgnoreProperties(ignoreUnknown = false) を使用してクラスごとに設定します。

さらに、ObjectMapper は、(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS を無効にして) 日付と時刻を ISO-8601 でフォーマットします 。

Jackson のデフォルトの動作を復元するには、application.propertiesquarkus.jackson.write-dates-as-timestamps=true を使用します。 単一フィールドのカスタム日付形式の場合は、@JsonFormat アノテーションを使用します。

Quarkus は、CDI Bean を介して Jackson の設定を簡素化します。さまざまな Jackson 設定を適用するには、io.quarkus.jackson.ObjectMapperCustomizer タイプの CDI Bean を作成します。カスタムモジュールを登録する例を次に示します。

@ApplicationScoped
public class MyObjectMapperCustomizer implements ObjectMapperCustomizer {
    @Override
    public void customize(ObjectMapper objectMapper) {
        // Add custom Jackson configuration here
    }
}

Jackson 設定を設定する場合は、このアプローチが推奨されます。

import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.jackson.ObjectMapperCustomizer;
import jakarta.inject.Singleton;

@Singleton
public class RegisterCustomModuleCustomizer implements ObjectMapperCustomizer {

    public void customize(ObjectMapper mapper) {
        mapper.registerModule(new CustomModule());
    }
}

ユーザーは、選択すれば自分の ObjectMapper Bean を提供することもできます。この場合、 ObjectMapper を生成する CDI プロデューサーの中で、すべての io.quarkus.jackson.ObjectMapperCustomizer Bean を手動で注入して適用することが非常に重要です。これを怠ると、様々なエクステンションによって提供される Jackson 固有のカスタマイズが適用されなくなります。

import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.arc.All;
import io.quarkus.jackson.ObjectMapperCustomizer;
import java.util.List;
import jakarta.inject.Singleton;

public class CustomObjectMapper {

    // Replaces the CDI producer for ObjectMapper built into Quarkus
    @Singleton
    ObjectMapper objectMapper(@All List<ObjectMapperCustomizer> customizers) {
        ObjectMapper mapper = myObjectMapper(); // Custom `ObjectMapper`

        // Apply all ObjectMapperCustomizer beans (incl. Quarkus)
        for (ObjectMapperCustomizer customizer : customizers) {
            customizer.customize(mapper);
        }

        return mapper;
    }
}

JSON-B

上記のように、 Quarkus では、 quarkus-resteasy-jsonb エクステンションを使用することで、 Jackson の代わりに JSON-B を使用するオプションを提供しています。

前のセクションで説明したのと同じアプローチに従って、io.quarkus.jsonb.JsonbConfigCustomizer Bean を使用して JSON-B を設定できます。

たとえば、 FooSerializer という名前のカスタムシリアライザーを com.example.Foo タイプで JSON-B で登録する必要がある場合、以下のような Bean を追加すれば十分です。

import io.quarkus.jsonb.JsonbConfigCustomizer;
import jakarta.inject.Singleton;
import jakarta.json.bind.JsonbConfig;
import jakarta.json.bind.serializer.JsonbSerializer;

@Singleton
public class FooSerializerRegistrationCustomizer implements JsonbConfigCustomizer {

    public void customize(JsonbConfig config) {
        config.withSerializers(new FooSerializer());
    }
}

より高度なオプションは、jakarta.json.bind.JsonbConfig の Bean を (Dependent スコープで) 直接提供するか、極端な場合、jakarta.json.bind.Jsonb タイプの Bean を (Singleton スコープで) 提供することでしょう。 後者の方法を利用する場合、jakarta.json.bind.Jsonb を生成する CDI プロデューサーにおいて、すべての io.quarkus.jsonb.JsonbConfigCustomizer Bean を手動で注入し適用することが非常に重要です。 これを怠ると、さまざまなエクステンションが提供する JSON-B 固有のカスタマイズが適用されなくなります。

import io.quarkus.jsonb.JsonbConfigCustomizer;

import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Instance;
import jakarta.json.bind.JsonbConfig;

public class CustomJsonbConfig {

    // Replaces the CDI producer for JsonbConfig built into Quarkus
    @Dependent
    JsonbConfig jsonConfig(Instance<JsonbConfigCustomizer> customizers) {
        JsonbConfig config = myJsonbConfig(); // Custom `JsonbConfig`

        // Apply all JsonbConfigCustomizer beans (incl. Quarkus)
        for (JsonbConfigCustomizer customizer : customizers) {
            customizer.customize(config);
        }

        return config;
    }
}

HAL 標準は、ウェブリンクを表現するためのシンプルなフォーマットです。

HAL のサポートを有効にするには、 quarkus-hal エクステンションをプロジェクトに追加します。また、 HAL は JSON サポートを必要とするため、 quarkus-resteasy-jsonb または quarkus-resteasy-jackson のいずれかのエクステンションモジュールを追加する必要があります。

Table 1. Table Contect オブジェクト
GAV 使用方法

io.quarkus:quarkus-hal

HAL

エクステンションを追加したら、次は REST リソースにアノテーションを付け、メディアタイプ application/hal+json (または RestMediaType.APPLICATION_HAL_JSON を使用) を生成できるようにします。例えば、以下のようになります。

@Path("/records")
public class RecordsResource {

    @GET
    @Produces({ MediaType.APPLICATION_JSON, "application/hal+json" })
    @LinkResource(entityClassName = "org.acme.Record", rel = "list")
    public List<TestRecord> getAll() {
        // ...
    }

    @GET
    @Path("/first")
    @Produces({ MediaType.APPLICATION_JSON, "application/hal+json" })
    @LinkResource(rel = "first")
    public TestRecord getFirst() {
        // ...
    }
}

これで、エンドポイント /records/records/first は、jsonhal+json の両方のメディアタイプを受け入れ、レコードを Hal 形式で出力するようになります。

例えば、 curl を使用して /records エンドポイントを呼び出してレコードのリストを返す場合、HAL 形式は次のようになります。

& curl -H "Accept:application/hal+json" -i localhost:8080/records
{
    "_embedded": {
        "items": [
            {
                "id": 1,
                "slug": "first",
                "value": "First value",
                "_links": {
                    "list": {
                        "href": "http://localhost:8081/records"
                    },
                    "first": {
                        "href": "http://localhost:8081/records/first"
                    }
                }
            },
            {
                "id": 2,
                "slug": "second",
                "value": "Second value",
                "_links": {
                    "list": {
                        "href": "http://localhost:8081/records"
                    },
                    "first": {
                        "href": "http://localhost:8081/records/first"
                    }
                }
            }
        ]
    },
    "_links": {
        "list": {
            "href": "http://localhost:8081/records"
        }
    }
}

When we call a resource /records/first that returns only one instance, then the output is:

& curl -H "Accept:application/hal+json" -i localhost:8080/records/first
{
    "id": 1,
    "slug": "first",
    "value": "First value",
    "_links": {
        "list": {
            "href": "http://localhost:8081/records"
        },
        "first": {
            "href": "http://localhost:8081/records/first"
        }
    }
}

フロントエンドの作成

それでは、FruitResource とやり取りするための簡単なウェブページを追加してみましょう。 Quarkus は、META-INF/resources ディレクトリーの下にある静的リソースを自動的に提供します。 src/main/resources/META-INF/resources ディレクトリーに、この fruits.html ファイルのコンテンツを含む fruits.html ファイルを追加します。

これで、REST サービスと対話できるようになりました。

  • 以下のように Quarkus を起動します。

    コマンドラインインタフェース
    quarkus dev
    Maven
    ./mvnw quarkus:dev
    Gradle
    ./gradlew --console=plain quarkusDev
  • ブラウザーで http://localhost:8080/fruits.html を開きます。

  • フォームを使って新しい果物をリストに追加します。

ネイティブ実行可能ファイルのビルド

以下のコマンドでネイティブの実行可能ファイルをビルドすることができます。

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

実行は簡単で、 ./target/rest-json-quickstart-1.0-SNAPSHOT-runner を実行するだけです。

その後、ブラウザーで http://localhost:8080/fruits.html を開いてアプリケーションを使用します。

シリアライゼーションについて

JSON シリアライズライブラリーは、 Java のリフレクションを使用してオブジェクトのプロパティーを取得してシリアライズします。

GraalVM でネイティブ実行可能ファイルを使用する場合、リフレクションで使用されるすべてのクラスを登録する必要があります。 幸いなことに、ほとんどの場合、Quarkus がその作業を自動的に実行します。 これまでのところ、Fruit を含め、リフレクションを使用するためのクラスを 1 つも登録していませんが、すべて正常に動作しています。

Quarkusは、RESTメソッドからシリアライズされた型を推論することができる場合に、何らかのマジックを実行します。以下のようなRESTメソッドがある場合、Quarkusは、 Fruit がシリアライズされると判断します。

@GET
public List<Fruit> list() {
    // ...
}

Quarkus は、ビルド時に REST メソッドを分析することで、このような処理を自動的に実行します。そのため、このガイドの最初の部分ではリフレクション登録は必要ありませんでした。

Jakarta RESTの世界でよく見られるもう一つのパターンは、 Response オブジェクトを使うことです。 Response には、いくつかの良い特典があります:

  • メソッドで何が起こるかによって異なるエンティティータイプを返すことができます (たとえば LegumeError)。

  • Response の属性を設定できます (エラーが発生したときにステータスを知ることができます)。

REST メソッドは次のようになります。

@GET
public Response list() {
    // ...
}

Quarkus は、情報が利用できないため、ビルド時に Response に含まれるタイプを判別できません。 この場合、Quarkus は必要なクラスにリフレクションを自動的に登録できなくなります。

これが次のセクションにつながります。

レスポンスの使用

Fruit クラスと同じモデルに従って、JSON としてシリアライズされる Legume クラスを作成してみましょう。

package org.acme.rest.json;

public class Legume {

    public String name;
    public String description;

    public Legume() {
    }

    public Legume(String name, String description) {
        this.name = name;
        this.description = description;
    }
}

それでは、マメ科植物のリストを返すメソッドを 1 つだけ持つ LegumeResource REST サービスを作成してみましょう。

このメソッドは Legume のリストではなく Response を返ます。

package org.acme.rest.json;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

@Path("/legumes")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class LegumeResource {

    private Set<Legume> legumes = Collections.synchronizedSet(new LinkedHashSet<>());

    public LegumeResource() {
        legumes.add(new Legume("Carrot", "Root vegetable, usually orange"));
        legumes.add(new Legume("Zucchini", "Summer squash"));
    }

    @GET
    public Response list() {
        return Response.ok(legumes).build();
    }
}

ここで、マメ科植物のリストを表示するための簡単なウェブページを追加してみましょう。 src/main/resources/META-INF/resources ディレクトリーに、この legumes.html ファイルの内容を含む legumes.html ファイルを追加します。

ブラウザーを開いて http://localhost:8080/legumes.html にアクセスすると、マメ科植物のリストが表示されます。

興味深いのは、アプリケーションをネイティブ実行可能ファイルとして実行するときです。

  • 以下でネイティブの実行可能ファイルを作成します。

    コマンドラインインタフェース
    quarkus build --native
    Maven
    ./mvnw install -Dnative
    Gradle
    ./gradlew build -Dquarkus.native.enabled=true
  • ./target/rest-json-quickstart-1.0-SNAPSHOT-runner で実行します。

  • ブラウザーを起動し、 http://localhost:8080/legumes.html にアクセスします。

そこにはマメ科植物はありません。

上記のように、問題は Quarkus が REST エンドポイントを分析することで Legume クラスが何らかのリフレクションを必要とすることを判断できなかったことです。 JSON シリアライズライブラリーは、Legume のフィールドのリストを取得しようとすると空のリストを取得するため、フィールドのデータをシリアライズしません。

現時点では、JSON-B や Jackson がクラスのフィールドのリストを取得しようとしたときに、そのクラスがリフレクションに登録されていない場合、例外は出力されません。 GraalVM は空のフィールドのリストを返します。

将来的にはこの点が変わり、エラーがより明白になることが期待されます。

Legume クラスに @RegisterForReflection アノテーションを追加することで、手動で Legume を リフレクション用に登録することができます。

import io.quarkus.runtime.annotations.RegisterForReflection;

@RegisterForReflection
public class Legume {
    // ...
}
@RegisterForReflection アノテーションは、ネイティブコンパイル時にクラスとそのメンバーを保持するよう Quarkus に指示します。 @RegisterForReflection アノテーションの詳細については、 ネイティブアプリケーションのヒントのページを参照してください。

それを実行して、今までと同じ手順を踏んでみましょう。

  • Ctrl+C でアプリケーションを停止させます。

  • 以下でネイティブの実行可能ファイルを作成します。

    コマンドラインインタフェース
    quarkus build --native
    Maven
    ./mvnw install -Dnative
    Gradle
    ./gradlew build -Dquarkus.native.enabled=true
  • ./target/rest-json-quickstart-1.0-SNAPSHOT-runner で実行します。

  • ブラウザーを起動し、 http://localhost:8080/legumes.html にアクセスします。

今回はマメ科植物の一覧が表示されました。

リアクティブであること

リアクティブワークロードの場合は、常に Quarkus REST を使用してください。

非同期処理を処理するために リアクティブ型 を返すことができます。Quarkusでは、リアクティブで非同期なコードを書くために Mutiny の使用を推奨しています。

Mutiny と RESTEasy を統合するには、 quarkus-resteasy-mutiny 依存関係をプロジェクトに追加する必要があります。

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

そして、エンドポイントは UniMulti のインスタンスを返すことができます。

@GET
@Path("/{name}")
public Uni<Fruit> getOne(@PathParam String name) {
    return findByName(name);
}

@GET
public Multi<Fruit> getAll() {
    return findAll();
}

単一の結果がある場合は Uni を使用します。 Multi は、非同期的に放出される可能性のある複数の項目がある場合に使用します。

Uni<Response> のように、 UniResponse を使用して、非同期 HTTP レスポンスを返すことができます。

Mutiny についての詳細は、 Mutiny - 直感的なリアクティブプログラミングライブラリー に記載されています。

HTTP フィルターとインターセプター

HTTP リクエストとレスポンスの両方とも、それぞれ ContainerRequestFilter または ContainerResponseFilter の実装を提供することで、インターセプトすることができます。 これらのフィルターは、メッセージに関連付けられたメタデータ (HTTP ヘッダー、クエリパラメーター、メディアタイプ、その他のメタデータ) を処理するのに適しています。 また、ユーザーがエンドポイントにアクセスする権限を持っていない場合などに、リクエスト処理を中止することもできます。

Let’s use ContainerRequestFilter to add logging capability to our service. We can do that by implementing ContainerRequestFilter and annotating it with the @Provider annotation:

package org.acme.rest.json;

import io.vertx.core.http.HttpServerRequest;
import org.jboss.logging.Logger;

import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.ext.Provider;

@Provider
public class LoggingFilter implements ContainerRequestFilter {

    private static final Logger LOG = Logger.getLogger(LoggingFilter.class);

    @Context
    UriInfo info;

    @Context
    HttpServerRequest request;

    @Override
    public void filter(ContainerRequestContext context) {

        final String method = context.getMethod();
        final String path = info.getPath();
        final String address = request.remoteAddress().toString();

        LOG.infof("Request %s %s from IP %s", method, path, address);
    }
}

これで、 REST メソッドが呼び出されるたびに、リクエストがログとしてコンソールに出力されるようになりました。

2019-06-05 12:44:26,526 INFO  [org.acm.res.jso.LoggingFilter] (executor-thread-1) Request GET /legumes from IP 127.0.0.1
2019-06-05 12:49:19,623 INFO  [org.acm.res.jso.LoggingFilter] (executor-thread-1) Request GET /fruits from IP 0:0:0:0:0:0:0:1
2019-06-05 12:50:44,019 INFO  [org.acm.res.jso.LoggingFilter] (executor-thread-1) Request POST /fruits from IP 0:0:0:0:0:0:0:1
2019-06-05 12:51:04,485 INFO  [org.acm.res.jso.LoggingFilter] (executor-thread-1) Request GET /fruits from IP 127.0.0.1

CORS フィルター

クロスオリジンリソース共有 (CORS)は、ウェブページ上の制限されたリソースを、最初のリソースが提供されたドメイン以外の別のドメインから要求できるようにするメカニズムです。

Quarkus には、HTTP レイヤーレベルの CORS フィルターが含まれています。 CORS フィルターとその使用方法の詳細は、Quarkus のクロスオリジンリソース共有ガイドの CORS フィルター セクションを参照してください。

GZipのサポート

Quarkus では、デフォルトでは有効になっていませんが GZip をサポートしています。以下のプロパティーを使用して、GZipのサポートを設定することができます。

quarkus.resteasy.gzip.enabled=true (1)
quarkus.resteasy.gzip.max-input=10M (2)
1 Gzipサポートを有効にします。
2 圧縮されたリクエストボディーの上限を設定します。 これは、攻撃の範囲を制限することで潜在的な攻撃を軽減するのに役立ちます。デフォルト値は 10M です。 この設定オプションは、[0-9]+[KkMmGgTtPpEeZzYy]? というフォーマット (正規表現として表示) の文字列を認識します。 接尾辞が指定されていない場合は、バイトであると想定されます。

GZip サポートが有効になったら、エンドポイントメソッドに @org.jboss.resteasy.annotations.GZIP アノテーションを追加することで、エンドポイントで GZip サポートを使用できます。

また、HTTP レスポンスの圧縮をグローバルに有効にする設定プロパティー (quarkus.http.enable-compression) もあります。 有効な場合、Content-Type HTTP ヘッダーが設定され、その値が quarkus.http.compress-media-types 設定プロパティーで設定された圧縮メディアタイプである場合、レスポンスボディーが圧縮されます。

マルチパートのサポート

RESTEasy は RESTEasy Multipart Provider を介してマルチパートをサポートしています。

Quarkusは 、 quarkus-resteasy-multipart というエクステンションを提供しています。

このエクステンションは RESTEasy のデフォルトの動作とは若干異なり、リクエストで何も指定されていない場合のデフォルトの文字セットは US-ASCII ではなく UTF-8 となります。

この動作は、以下のプロパティーで設定できます。

ビルド時に固定される設定プロパティ - 他のすべての設定プロパティは実行時にオーバーライド可能

Configuration property

デフォルト

Default charset.

Note that the default value is UTF-8 which is different from RESTEasy’s default value US-ASCII.

Environment variable: QUARKUS_RESTEASY_MULTIPART_INPUT_PART_DEFAULT_CHARSET

Show more

Charset

UTF-8

The default content-type.

Environment variable: QUARKUS_RESTEASY_MULTIPART_INPUT_PART_DEFAULT_CONTENT_TYPE

Show more

string

text/plain

サーブレットとの互換性

Quarkus では、 RESTEasy は Vert.x HTTP サーバーの上で直接実行するか、サーブレットに依存している場合は Undertow の上で実行することができます。

そのため、HttpServletRequest などの特定のクラスは常に注入できるとは限りません。 このような特定のクラスのユースケースは、リモートクライアントの IP を取得する場合を除き、Jakarta REST の同等の機能でほとんどカバーされています。

RESTEasy には、注入できる代替 API HttpRequest が付属しています。この API には、この問題を解決するためのメソッド getRemoteAddress() および getRemoteHost() があります。

RESTEasy と RESTクライアントのやりとり

Quarkus では、RESTEasy エクステンションと REST クライアントエクステンション が同じインフラストラクチャーを共有します。 この考慮事項の重要な影響の 1 つは、それらが (Jakarta REST の意味において) 同じプロバイダーのリストを共有することです。

たとえば、WriterInterceptor を宣言すると、デフォルトではサーバーの呼び出しとクライアントの呼び出しの両方がインターセプトされますが、これは望ましい動作ではない可能性があります。

しかし、このデフォルトの動作を変更してプロバイダーに制約を設けることができます。

  • プロバイダーに @ConstrainedTo(RuntimeType.SERVER) アノテーションを追加することで、 サーバー の呼び出しのみを考慮します。

  • プロバイダーに @ConstrainedTo(RuntimeType.CLIENT) アノテーションを追加することで、 クライアント の呼び出しのみを考慮します。

Jakarta EE 開発との違い

Application クラスは不要

アプリケーションが提供する Application のサブクラスによる設定もサポートされていますが、必須ではありません。

単一のJakarta RESTアプリケーション限定

標準的なサーブレットコンテナで動作する Jakarta REST (および RESTEasy) とは対照的に、Quarkus は単一の Jakarta REST アプリケーションのデプロイメントのみをサポートしています。 複数の Jakarta REST Application クラスが定義されている場合、ビルドは失敗し、Multiple classes have been annotated with @ApplicationPath which is currently not supported というメッセージが表示されます。

複数の Jakarta REST アプリケーションが定義されている場合、プロパティ quarkus.resteasy.ignore-application-classes=true を使用すると、すべての明示的な Application クラスを無視することができます。これにより、すべてのリソース・クラスは、 quarkus.resteasy.path (デフォルト: / )で定義されるapplication-pathを介して利用可能になります。

Jakarta RESTアプリケーションのサポートの制限

RESTEasy エクステンションは、クラス jakarta.ws.rs.core.Application のメソッド getProperties() をサポートしていません。 さらに、メソッド getClasses() および getSingletons() のみに依存して、アノテーションが付けられたリソース、プロバイダー、および機能クラスをフィルタリングします。 組み込みのリソース、プロバイダー、機能クラスや、他のエクステンションで登録されたリソース、プロバイダ、機能クラスは除外されません。 最後に、メソッド getSingletons() が返すオブジェクトは無視され、リソース、プロバイダー、機能クラスをフィルタリングするためにクラスのみが考慮されます。言い換えれば、メソッド getSingletons() は実際には getClasses() と同じように管理されます。

リソースのライフサイクル

Quarkus では、すべての Jakarta REST リソースは CDI Bean として扱われます。@Inject を使って他の Bean を注入したり、@Transactional などのバインディングを使ってインターセプターをバインドしたり、@PostConstruct コールバックを定義したりすることが可能です。

リソースクラスにスコープアノテーションが宣言されていない場合、スコープはデフォルトに設定されます。 quarkus.resteasy.singleton-resources プロパティーはデフォルトのスコープを制御できます。

true (デフォルト) に設定すると、すべてのリクエストを処理するためにリソースクラスの 単一のインスタンス (@jakarta.inject.Singleton で定義) が作成されます 。

false に設定すると、リクエストごとにリソースクラスの 新しいインスタンス が作成されます。

明示的な CDI スコープアノテーション (@RequestScoped@ApplicationScoped など) は、常にデフォルトの動作をオーバーライドし、リソースインスタンスのライフサイクルを指定します。

@Context 要素は CDI によっては注入されないため、コンストラクター注入によって注入することはできません。 代わりに、リソースのフィールドに @Context 要素を注入します。

ビルド時条件でのJakarta RESTのクラスのインクルード/エクスクルード

Quarkusでは、CDI Beanと同様に、ビルド時の条件によって、Jakarta RESTリソース、プロバイダ、フィーチャーを直接取り込んだり除外したりすることができます。したがって、さまざまなJakarta RESTクラスにプロファイル条件( @io.quarkus.arc.profile.IfBuildProfile または @io.quarkus.arc.profile.UnlessBuildProfile )やプロパティ条件( io.quarkus.arc.properties.IfBuildProperty または io.quarkus.arc.properties.UnlessBuildProperty )をアノテーションして、ビルド時にQuarkusに対して、どのような条件の下でJakarta RESTクラスを含めるべきかを示すことができます。

次の例では、 Quarkus は、ビルドプロファイル app1 が有効になっている場合に限り、エンドポイント sayHello を含めます。

@IfBuildProfile("app1")
public class ResourceForApp1Only {

    @GET
    @Path("sayHello")
    public String sayHello() {
        return "hello";
     }
}

Jakarta REST Applicationが検出され、メソッド getClasses()getSingletons() がオーバーライドされている場合、Quarkusはビルド時の条件を無視し、Jakarta REST Applicationで定義されているもののみを考慮することに注意してください。

まとめ

Quarkus を使用した JSON REST サービスの作成は、実績のあるよく知られたテクノロジーに依存しているため、簡単に行えます。

いつものように、 Quarkus は、アプリケーションをネイティブ実行可能ファイルとして実行する際に、内部の作業をさらに簡略化しています。

Response を使用していて、Quarkus がシリアライズされている Bean を特定できない場合は、 @RegisterForReflection を使ってアノテーションを付ける必要があることに注意してください。

関連コンテンツ