RESTEasy Classic

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

従来のブロック型ワークロードとリアクティブ型ワークロードの両方を同等にサポートするRESTEasy Reactiveを使用することが推奨されるようになりました。RESTEasy Reactiveの詳細については、 REST JSON入門ガイド または RESTEasy Reactiveのリファレンスドキュメント をご覧ください。

there is another guide if you need a REST client based on RESTEasy Classic (including support for 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.2.12.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rest-json-quickstart \
    -Dextensions='resteasy-jackson' \
    -DnoCode
cd rest-json-quickstart

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

このコマンドは、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.2.12.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rest-json-quickstart \
    -Dextensions='resteasy-jsonb' \
    -DnoCode
cd rest-json-quickstart

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

このコマンドは、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-jackson や quarkus-resteasy-jsonb などの JSON エクステンションがインストールされている場合、メディアタイプが @Produces や @Consumes アノテーションで明示的に設定されていない限り、Quarkus はほとんどの戻り値に application/json メディアタイプをデフォルトで使用します( String や File などのよく知られたタイプには例外があり、それぞれ text/plain と application/octet-stream がデフォルトとなっています)。

デフォルトでJSONを使用したくない場合は、 quarkus.resteasy-json.default-json=false を設定すると、デフォルトは自動ネゴシエーションに戻ります。これを設定した場合、JSON を使用するためには @Produces(MediaType.APPLICATION_JSON) と @Consumes(MediaType.APPLICATION_JSON) をエンドポイントに追加する必要があります。

JSONのデフォルトに依存しない場合は、エンドポイントに @Produces と @Consumes のアノテーションを付けて、期待されるcontent-typesを正確に定義することが強く推奨されています。これにより、ネイティブ実行可能ファイルに含まれる Jakarta REST プロバイダー(コンバーターと見なすことができます)の数を減らすことができます。

JSON サポートの設定

Jackson

Quarkusでは、CDI経由で取得した(そしてQuarkusのエクステンションによって消費される)デフォルトのJackson ObjectMapper は、未知のプロパティーを無視するように設定されています( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 機能を無効にすることで)。

application.properties で quarkus.jackson.fail-on-unknown-properties=true を設定するか、 クラスごとに @JsonIgnoreProperties(ignoreUnknown = false) を設定することで、Jackson のデフォルトの動作を復元することができます。

さらに、 ObjectMapper 、日付と時刻をISO-8601でフォーマットするように設定されています( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS の機能を無効にすることで)。

application.properties で quarkus.jackson.fail-on-unknown-properties=true を設定するか、 @JsonIgnoreProperties(ignoreUnknown = false) を経由してクラスごとに設定することで、Jackson のデフォルトの動作を復元することができます。

また、Quarkusは、CDI Bean を介して、様々なJackson設定を非常に簡単に設定することができます。最も単純な(そして推奨される)アプローチは、 io.quarkus.jackson.ObjectMapperCustomizer 型の CDI Bean を定義し、その中で、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 は、メディアタイプ json と hal+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を起動します。

    CLI
    quarkus dev
    Maven
    ./mvnw quarkus:dev
    Gradle
    ./gradlew --console=plain quarkusDev
  • http://localhost:8080/fruits.html をブラウザで開く

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

ネイティブ実行可能ファイルの構築

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

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native

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

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

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

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

GraalVMでネイティブ実行可能ファイルを使用する場合、リフレクションで使用されるすべてのクラスを登録する必要があります。良いニュースは、Quarkusがほとんどの場合、その作業を代行してくれるということです。これまでのところ、 Fruit でさえ、リフレクトを使用するためのクラスを登録しておらず、すべてが正常に動作しています。

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

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

Quarkusは、ビルド時にRESTメソッドを分析することで、自動的にそのような処理を行ってくれます。

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

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

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

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

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

Response に含まれるタイプは情報がないため、Quarkusがビルド時に判断することはできません。この場合、Quarkusは必要なクラスを自動的に反映登録することができません。

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

Response の利用

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;
    }
}

それでは、マメ科植物のリストを返すメソッドを一つだけ持つ 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 にアクセスすると、マメ科植物のリストが表示されます。

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

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

    CLI
    quarkus build --native
    Maven
    ./mvnw install -Dnative
    Gradle
    ./gradlew build -Dquarkus.package.type=native
  • ./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 {
    // ...
}
The @RegisterForReflection annotation instructs Quarkus to keep the class and its members during the native compilation. More details about the @RegisterForReflection annotation can be found on the native application tips page.

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

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

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

    CLI
    quarkus build --native
    Maven
    ./mvnw install -Dnative
    Gradle
    ./gradlew build -Dquarkus.package.type=native
  • ./target/rest-json-quickstart-1.0-SNAPSHOT-runner で実行します。

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

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

リアクティブであること

リアクティブなワークロードには、常に RESTEasy Reactive を使用してください。

非同期処理を処理するために リアクティブ型 を返すことができます。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")

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

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

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

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

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

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

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

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

ContainerRequestFilter を使用して、サービスにロギング機能を追加してみましょう。 ContainerRequestFilter を実装して、 @Provider アノテーションをつけることで実現できます。

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 には、 CORS フィルターが付属しています。使用方法については、 HTTP リファレンスドキュメント を参照してください。

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 アノテーションを追加することで、エンドポイントで使用することができます。

There is also the quarkus.http.enable-compression configuration property which enables HTTP response compression globally. If enabled then a response body is compressed if the Content-Type HTTP header is set and the value is a compressed media type as configured via the quarkus.http.compress-media-types configuration property.

マルチパートのサポート

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

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

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

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

Configuration property fixed at build time - All other configuration properties are overridable at runtime

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 これは、getRemoteAddress() メソッドと getRemoteHost() メソッドを持っており、この問題を解決することができます。

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

Quarkusでは、RESTEasyエクステンションと REST Clientエクステンション は、同じインフラストラクチャを共有しています。この考慮の重要な結果の1つは、(Jakarta RESTの意味での)プロバイダーのリストを共有することです。

例えば、 WriterInterceptor を宣言した場合、デフォルトではサーバーの呼び出しとクライアントの呼び出しの両方をインターセプトしますが、これは望ましい動作ではないかもしれません。

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

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

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

What’s Different from Jakarta EE Development

No Need for Application Class

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 など)は、常にデフォルトの動作を上書きし、リソースインスタンスのライフサイクルを指定します。

ビルド時条件での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 を使ってアノテーションを付ける必要があります。