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 について扱います。

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

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 オプションを追加します。

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

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

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

Windowsユーザーの場合:

  • If using cmd, (don’t use backward slash \ and put everything on the same line)

  • If using Powershell, wrap -D parameters in double quotes e.g. "-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 オプションを追加します。

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

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

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

Windowsユーザーの場合:

  • If using cmd, (don’t use backward slash \ and put everything on the same line)

  • If using Powershell, wrap -D parameters in double quotes e.g. "-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 エクステンションがインストールされている場合、メディアタイプが @Produces@Consumes アノテーションで明示的に設定されていない限り、Quarkus はほとんどの戻り値に application/json メディアタイプをデフォルトで使用します( StringFile などのよく知られたタイプには例外があり、それぞれ text/plainapplication/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.propertiesquarkus.jackson.fail-on-unknown-properties=true を設定するか、 クラスごとに @JsonIgnoreProperties(ignoreUnknown = false) を設定することで、Jackson のデフォルトの動作を復元することができます。

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

application.propertiesquarkus.jackson.write-dates-as-timestamps=true を設定すると、Jackson のデフォルトの動作を復元することができます。単一のフィールドのフォーマットを変更したい場合は、@JsonFormat アノテーションを使用することができます。

また、 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 は、メディアタイプ 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"
        }
    }
}

フロントエンドの作成

Now let’s add a simple web page to interact with our FruitResource. Quarkus automatically serves static resources located under the META-INF/resources directory. In the src/main/resources/META-INF/resources directory, add a fruits.html file with the content from this fruits.html file in it.

これで、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.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 には、いくつかの良い特典があります:

  • メソッド内で発生した内容に応じて異なるエンティティータイプを返すことができます ( 例えば LegumeError ) 。

  • Response の属性 ( エラーの場合は、そのステータスなど ) を設定することができます。

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

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

Response に含まれるタイプは情報がないため、Quarkusがビルド時に判断することはできません。この場合、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;
    }
}

それでは、マメ科植物 ( Legume ) のリストを返すメソッドを一つだけ持つ 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.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 {
    // ...
}
@RegisterForReflection アノテーションは、ネイティブコンパイル時にクラスとそのメンバーを保持するよう Quarkus に指示します。 @RegisterForReflection アノテーションの詳細については、 ネイティブアプリケーションのヒントのページを参照してください。

上記を反映して、今までと同じ手順を実行してみます。

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

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

    コマンドラインインタフェース
    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")

そして、エンドポイントは 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 ヘッダー、クエリパラメーター、メディアタイプ、その他のメタデータです。また、ユーザーがエンドポイントにアクセスする権限を持っていない場合など、リクエスト処理を中止する機能も持っています。

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

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

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

Quarkusでは、RESTEasyエクステンションと REST Clientエクステンション は、同じインフラストラクチャを共有しています。この考慮の重要な結果の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 など)は、常にデフォルトの動作を上書きし、リソースインスタンスのライフサイクルを指定します。

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