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

Reactive Routesの使用

Reactive routesは、HTTPエンドポイントを実装するための代替アプローチを提案するもので、 ルート を宣言してチェインさせるものです。このアプローチは、Express.JsやHapiのようなフレームワークで、JavaScriptの世界で非常に人気がありました。Quarkusも、リアクティブルートを使用する可能性を提供します。ルートだけでREST APIを実装したり、Jakarta RESTリソースやサーブレットと組み合わせたりすることができます。

このガイドで紹介するコードは、この GitHubリポジトリreactive-routes-quickstart ディレクトリ にあります。

Reactive Routesは当初、 Quarkus Reactive Architecture の上に、HTTP APIのリアクティブな実行モデルを提供するために導入されました。 RESTEasy Reactive の導入により、リアクティブなHTTP APIを実装しても、Jakarta RESTアノテーションを使用することができるようになりました。特に、より ルートベースの アプローチや、基盤となるリアクティブエンジンに近いものを求める場合は、リアクティブルートが引き続きサポートされます。

Quarkus HTTP

先に進む前に、QuarkusのHTTPレイヤーを見てみましょう。QuarkusのHTTPサポートは、ノンブロッキングでリアクティブなエンジン(Eclipse Vert.xとNetty)に基づいています。アプリケーションが受け取るすべてのHTTPリクエストは、 イベントループ (I/Oスレッド)によって処理され、リクエストを管理するコードに向かってルーティングされます。宛先に応じて、ワーカースレッド(Servlet、Jax-RS)でリクエストを管理するコードを呼び出すか、IOスレッド(Reactive Routes)を使用します。このため、Reactive Routesはノンブロッキングであるか、そのブロッキング性を明示的に宣言しなければならないことに注意してください (ワーカースレッド上で呼び出されることで結果的にブロッキングされます)。

Quarkus HTTP Architecture

このトピックの詳細については、Quarkus リアクティブアーキテクチャのドキュメント を参照してください。

Reactive Routesの宣言

Reactive Routes を使用する最初の方法は、@Route アノテーションを使用することです。このアノテーションにアクセスするには、次のコマンドを実行し quarkus-reactive-routes エクステンションを追加する必要があります。

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

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

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

そして、 Bean では、以下のように @Route アノテーションを使用することができます。

package org.acme.reactive.routes;

import io.quarkus.vertx.web.Route;
import io.quarkus.vertx.web.Route.HttpMethod;
import io.quarkus.vertx.web.RoutingExchange;
import io.vertx.ext.web.RoutingContext;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped (1)
public class MyDeclarativeRoutes {

    // neither path nor regex is set - match a path derived from the method name
    @Route(methods = Route.HttpMethod.GET) (2)
    void hello(RoutingContext rc) { (3)
        rc.response().end("hello");
    }

    @Route(path = "/world")
    String helloWorld() { (4)
        return "Hello world!";
    }

    @Route(path = "/greetings", methods = Route.HttpMethod.GET)
    void greetingsQueryParam(RoutingExchange ex) { (5)
        ex.ok("hello " + ex.getParam("name").orElse("world")); (6)
    }

    @Route(path = "/greetings/:name", methods = Route.HttpMethod.GET) (7)
    void greetingsPathParam(@Param String name, RoutingExchange ex) {
        ex.ok("hello " + name);
    }
}
1 スコープアノテーションがないクラスでリアクティブルートが見つかった場合、 @jakarta.inject.Singleton が自動的に追加されます。
2 @Route アノテーションは、メソッドがリアクティブルートであることを示します。繰り返しになりますが、デフォルトでは、メソッドに含まれるコードはブロックしてはいけません。
3 このメソッドは、 RoutingContext をパラメーターとして取得します。 RoutingContext から HTTP リクエストを ( request() を使用して) 取得し、 response().end(…​) を使用してレスポンスを書き込むことができます。
4 アノテーションされたメソッドが void を返さない場合、引数はオプションです。
5 RoutingExchange は、いくつかの便利なメソッドを提供する RoutingContext の便利なラッパーです。
6 RoutingExchange は、リクエストクエリパラメータ name を取得するために使用されます。
7 パスはパラメータ name を定義します。パラメータ name@Param アノテーションを使用してメソッドパラメータ内に注入することができます 。

RoutingContext の使用の詳細については、 Vert.x Web ドキュメント を参照してください。

@Route アノテーションを使用すると、以下を設定できます。

  • path- Vert.x Web フォーマット を使用したパスによるルーティング用

  • regex - 正規表現を使用したルーティングについては、 詳細 を参照してください。

  • methods - GETPOST などのルートをトリガーする HTTP 動詞

  • type - normal (ノンブロッキング)、blocking (ワーカスレッドにディスパッチされるメソッド)、あるいは failure で、失敗時にこのルートが呼び出されることを示します。

  • order - 受信リクエストの処理に複数のルートが関与している場合のルートの順序。通常のユーザールートでは正である必要があります。

  • producesconsumes を使用して生成および消費された mime タイプ。

たとえば、次のようにブロッキングルートを宣言できます。

@Route(methods = HttpMethod.POST, path = "/post", type = Route.HandlerType.BLOCKING)
public void blocking(RoutingContext rc) {
    // ...
}

または、@io.smallrye.common.annotation.Blocking を使用して、type = Route.HandlerType.BLOCKING を省略できます。

@Route(methods = HttpMethod.POST, path = "/post")
@Blocking
public void blocking(RoutingContext rc) {
    // ...
}

When @Blocking is used, the type attribute of the @Route is ignored.

@Route アノテーションは繰り返し可能であるため、1 つのメソッドに対して複数のルートを宣言できます。

@Route(path = "/first") (1)
@Route(path = "/second")
public void route(RoutingContext rc) {
    // ...
}
1 各ルートは、異なるパスやメソッドなどを使用できます。

もしcontent-typeヘッダーが設定されていなければ、acceptヘッダーを io.vertx.ext.web.RoutingContext.getAcceptableContentType() で定義されている Route produces属性の値と照合して、最も受け入れやすいコンテンツタイプを使用しようと試みます。

@Route(path = "/person", produces = "text/html") (1)
String person() {
    // ...
}
1 accept ヘッダーが text/html と一致する場合、コンテンツタイプを自動的に text/html に設定します。

Executing route on a virtual thread

You can annotate a route method with @io.smallrye.common.annotation.RunOnVirtualThread in order to execute it on a virtual thread. However, keep in mind that not everything can run safely on virtual threads. You should read the Virtual thread support reference carefully and get acquainted with all the details.

競合するルートの処理

特定のパスに複数のルートが一致する場合があります。次の例では、両方のルートが /accounts/me に一致しています。

@Route(path = "/accounts/:id", methods = HttpMethod.GET)
void getAccount(RoutingContext rc) {
  ...
}

@Route(path = "/accounts/me", methods = HttpMethod.GET)
void getCurrentUserAccount(RoutingContext rc) {
  ...
}

その結果、パスパラメーター idme に設定された状態で最初のルートが呼び出されます。競合を回避するには、order 属性を使用します。

@Route(path = "/accounts/:id", methods = HttpMethod.GET, order = 2)
void getAccount(RoutingContext rc) {
  ...
}

@Route(path = "/accounts/me", methods = HttpMethod.GET, order = 1)
void getCurrentUserAccount(RoutingContext rc) {
  ...
}

2 番目のルートに低い順序を与えることで、それが最初に評価されます。リクエストパスが一致する場合は呼び出され、一致しない場合は他のルートが評価されます。

@RouteBase

このアノテーションを使用して、クラスで宣言されたReactive Routesのデフォルトを設定できます。

@RouteBase(path = "simple", produces = "text/plain") (1) (2)
public class SimpleRoutes {

    @Route(path = "ping") // the final path is /simple/ping
    void ping(RoutingContext rc) {
        rc.response().end("pong");
    }
}
1 path 値は、Route#path() が使用されるクラスで宣言されたルートメソッドの接頭辞として使用されます。
2 produces() の値は、 Route#produces() が空のすべてのルートで、コンテンツベースのルーティングに使用されます。

Reactive Routesメソッド

ルートメソッドは、CDIBean の非プライベート非静的メソッドでなければなりません。アノテーションされたメソッドが void を返す場合は、少なくとも 1 つの引数を受け入れる必要があります。以下はサポート対象のタイプです。アノテーションされたメソッドが void を返さない場合、引数はオプションになります。

void を返すメソッドは、このルートへの応答または HTTP リクエストを 終了 する必要があります。RoutingExchange のメソッドの中には、それを行うものとそうでないものがあり、レスポンスの end () メソッドを自分で呼び出す必要があります。詳細については、JavaDoc を参照してください。

ルートメソッドは、次のタイプの引数を受け取ることができます。

  • io.vertx.ext.web.RoutingContext

  • io.quarkus.vertx.web.RoutingExchange

  • io.vertx.core.http.HttpServerRequest

  • io.vertx.core.http.HttpServerResponse

  • io.vertx.mutiny.core.http.HttpServerRequest

  • io.vertx.mutiny.core.http.HttpServerResponse

さらに、HttpServerRequest パラメーターを以下の型を使用した @io.quarkus.vertx.web.Param でアノテーションされたメソッドパラメーターに挿入できます。

パラメータータイプ 取得方法

java.lang.String

routingContext.request().getParam()

java.util.Optional<String>

routingContext.request().getParam()

java.util.List<String>

routingContext.request().params().getAll()

リクエストパラメーターの例
@Route
String hello(@Param Optional<String> name) {
   return "Hello " + name.orElse("world");
}

HttpServerRequest ヘッダーは、以下の型を使用した @io.quarkus.vertx.web.Header でアノテーションされたメソッドパラメーターに挿入できます。

パラメータータイプ 取得方法

java.lang.String

routingContext.request().getHeader()

java.util.Optional<String>

routingContext.request().getHeader()

java.util.List<String>

routingContext.request().headers().getAll()

リクエストヘッダーの例
@Route
String helloFromHeader(@Header("My-Header") String header) {
   return header;
}

リクエストボディは、以下の型を使用した、 @io.quarkus.vertx.web.Body でアノテーションされたメソッドパラメーターに注入することができます。

パラメータータイプ 取得方法

java.lang.String

routingContext.getBodyAsString()

io.vertx.core.buffer.Buffer

routingContext.getBody()

io.vertx.core.json.JsonObject

routingContext.getBodyAsJson()

io.vertx.core.json.JsonArray

routingContext.getBodyAsJsonArray()

その他のタイプ

routingContext.getBodyAsJson().mapTo(MyPojo.class)

リクエストボディの例
@Route(produces = "application/json")
Person createPerson(@Body Person person, @Param("id") Optional<String> primaryKey) {
  person.setId(primaryKey.map(Integer::valueOf).orElse(42));
  return person;
}

失敗ハンドラーは、タイプが Throwable を拡張する単一のメソッドパラメーターを宣言できます。パラメーターのタイプは、 RoutingContext#failure() の結果と一致させるために使用されます。

失敗ハンドラーの例
@Route(type = HandlerType.FAILURE)
void unsupported(UnsupportedOperationException e, HttpServerResponse response) {
  response.setStatusCode(501).end(e.getMessage());
}

Uni を返す

Reactive Routesでは、Uni を直接返すことができます。

@Route(path = "/hello")
Uni<String> hello() {
    return Uni.createFrom().item("Hello world!");
}

@Route(path = "/person")
Uni<Person> getPerson() {
    return Uni.createFrom().item(() -> new Person("neo", 12345));
}

リアクティブクライアントを使用している場合、Unis を返すと便利です。

@Route(path = "/mail")
Uni<Void> sendEmail() {
    return mailer.send(...);
}

返された Uni によって生成されるアイテムは以下のとおりです。

  • 文字列 - HTTP レスポンスに直接書き込まれます

  • io.vertx.core.buffer.Buffer - HTTP レスポンスに直接書き込まれます

  • オブジェクト - JSON にエンコードされた後に HTTP レスポンスに書き込まれます。まだ設定されていない場合、content-type ヘッダーは application/json に設定されます。

返された Uni が失敗した場合 (または null の場合)、HTTP 500 レスポンスが書き込まれます。

Uni<Void> を返すと 204 レスポンス (コンテンツなし) が生成されます。

結果を返す

結果を直接返すこともできます。

@Route(path = "/hello")
String helloSync() {
    return "Hello world";
}

Reactive Routesは IO スレッドで呼び出されるため、処理は 非ブロッキング である必要があることに注意してください。それ以外の場合は、@Route アノテーションの type 属性を Route.HandlerType.BLOCKING に設定するか、@io.smallrye.common.annotation.Blocking アノテーションを使用します。

メソッドは以下を返すことができます。

  • 文字列 - HTTP レスポンスに直接書き込まれます

  • io.vertx.core.buffer.Buffer - HTTP レスポンスに直接書き込まれます

  • オブジェクト - JSON にエンコードされた後に HTTP レスポンスに書き込まれます。まだ設定されていない場合、content-type ヘッダーは application/json に設定されます。

Multi を返す

Reactive Routesは Multi を返すことができます。レスポンスでは、項目が 1 つずつ書き込まれます。レスポンスの Transfer-Encoding ヘッダーは chunked に設定されます。

@Route(path = "/hello")
Multi<String> hellos() {
    return Multi.createFrom().items("hello", "world", "!");  (1)
}
1 helloworld! の生成

メソッドは以下を返すことができます。

  • Multi<String> - アイテムはレスポンスに 1 つずつ ( チャンク ごとに 1 つ) 書き込まれます。

  • Multi<Buffer> - バッファーは処理なしで 1 つずつ ( チャンク ごとに 1 つ) 書き込まれます。

  • Multi<Object> - アイテムは、 JSON にエンコードされ、レスポンスに 1 つずつ書き込まれるます。

@Route(path = "/people")
Multi<Person> people() {
    return Multi.createFrom().items(
            new Person("superman", 1),
            new Person("batman", 2),
            new Person("spiderman", 3));
}

前のスニペットは以下を生成します。

{"name":"superman", "id": 1} // chunk 1
{"name":"batman", "id": 2} // chunk 2
{"name":"spiderman", "id": 3} // chunk 3

JSON 配列アイテムのストリーミング

Multi を返して、JSON 配列を生成できます。その場合、すべてのアイテムはこの配列のアイテムです。レスポンスは、クライアントに青て、いごとに書き込まれます。これを行うには、produces 属性を "application/json" (または ReactiveRoutes.APPLICATION_JSON) に設定します。

@Route(path = "/people", produces = ReactiveRoutes.APPLICATION_JSON)
Multi<Person> people() {
    return Multi.createFrom().items(
            new Person("superman", 1),
            new Person("batman", 2),
            new Person("spiderman", 3));
}

前のスニペットは以下を生成します。

[
  {"name":"superman", "id": 1} // chunk 1
  ,{"name":"batman", "id": 2} // chunk 2
  ,{"name":"spiderman", "id": 3} // chunk 3
]
produces 属性は配列です。単一の値を渡す場合は、{" と "}" を省略できます。"application/json" は配列の最初の値でなければならないことに注意してください。

Multi<String>Multi<Object>Multi<Void> のみ JSON 配列に書き込むことができます。Multi<Void> を使用すると空の配列が生成されます。Multi<Buffer> は使用できません。Buffer を使用する必要がある場合は、最初にコンテンツを JSON または文字列表現に変換します。

asJsonArray の非推奨

ReactiveRoutes.asJsonArray は、Quarkus のセキュリティーレイヤーと互換性がないため、非推奨になりました。

イベントストリームとサーバー送信イベントのサポート

Multi を返して、イベントソース (サーバー送信イベントのストリーム) を生成できます。この機能を有効にするには、次のように produces 属性を "text/event-stream" (または ReactiveRoutes.EVENT_STREAM) に設定します。

@Route(path = "/people", produces = ReactiveRoutes.EVENT_STREAM)
Multi<Person> people() {
    return Multi.createFrom().items(
            new Person("superman", 1),
            new Person("batman", 2),
            new Person("spiderman", 3));
}

この方法では、以下が生成されます。

data: {"name":"superman", "id": 1}
id: 0

data: {"name":"batman", "id": 2}
id: 1

data: {"name":"spiderman", "id": 3}
id: 2
produces 属性は配列です。単一の値を渡す場合は、{" と "}" を省略できます。"text/event-stream" は配列の最初の値でなければならないことに注意してください。

io.quarkus.vertx.web.ReactiveRoutes.ServerSentEvent インターフェイスを実装して、サーバー送信イベントの event および id セクションをカスタマイズすることもできます。

class PersonEvent implements ReactiveRoutes.ServerSentEvent<Person> {
    public String name;
    public int id;

    public PersonEvent(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public Person data() {
        return new Person(name, id); // Will be JSON encoded
    }

    @Override
    public long id() {
        return id;
    }

    @Override
    public String event() {
        return "person";
    }
}

Multi<PersonEvent> を使用すると、以下が生成されます。

event: person
data: {"name":"superman", "id": 1}
id: 1

event: person
data: {"name":"batman", "id": 2}
id: 2

event: person
data: {"name":"spiderman", "id": 3}
id: 3
asEventStream の非推奨

ReactiveRoutes.asEventStream は、Quarkus のセキュリティーレイヤーと互換性がないため、非推奨になりました。

NDJSON 形式の Json ストリーム

Multi を返して、JSON 値の改行区切りストリームを生成できます。この機能を有効にするには、@Route アノテーションの produces 属性を "application/x-ndjson" (または ReactiveRoutes.ND_JSON) に設定します。

@Route(path = "/people", produces = ReactiveRoutes.ND_JSON)
Multi<Person> people() {
    return ReactiveRoutes.asJsonStream(Multi.createFrom().items(
            new Person("superman", 1),
            new Person("batman", 2),
            new Person("spiderman", 3)
            ));
}

この方法では、以下が生成されます。

{"name":"superman", "id": 1}
{"name":"batman", "id": 2}
{"name":"spiderman", "id": 3}
produces 属性は配列です。単一の値を渡す場合は、{" と "}" を省略できます。"application/x-ndjson" は配列の最初の値でなければならないことに注意してください。

オブジェクトの代わりに文字列を指定することもできます。その場合、文字列を引用符で囲うことで、有効な JSON 値になります。

@Route(path = "/people", produces = ReactiveRoutes.ND_JSON)
Multi<Person> people() {
    return ReactiveRoutes.asJsonStream(Multi.createFrom().items(
            "superman",
            "batman",
            "spiderman"
            ));
}
"superman"
"batman"
"spiderman"
asJsonStream の非推奨

ReactiveRoutes.asJsonStream は、Quarkus のセキュリティーレイヤーと互換性がないため、非推奨になりました。

Bean バリデーションの使用

Reactive Routesと Bean バリデーションを組み合わせることができます。まず、プロジェクトに quarkus-hibernate-validator エクステンションを必ず追加してください。次に、ルートパラメーターに制約を追加できます (@Param または @Body でアノテーションされます)。

@Route(produces = "application/json")
Person createPerson(@Body @Valid Person person, @NonNull @Param("id") String primaryKey) {
  // ...
}

パラメーターがテストに合格しなかった場合、HTTP 400 レスポンスを返します。リクエストが JSON ペイロードを受け入れる場合、レスポンスは 問題 のとおりの形式になります。

オブジェクトまたは Uni を返す場合、@Valid アノテーションも使用できます。

@Route(...)
@Valid Uni<Person> createPerson(@Body @Valid Person person, @NonNull @Param("id") String primaryKey) {
  // ...
}

ルートによって生成されたアイテムがバリデーションを通過しない場合、HTTP 500 レスポンスが返されます。リクエストが JSON ペイロードを受け入れる場合、レスポンスは 問題 のとおりの形式になります。

リターンタイプでは @Valid のみがサポートされていることに注意してください。返されるクラスは任意の制約を使用できます。Uni の場合、非同期で生成されたアイテムをチェックします。

Vert.x Web ルーターの使用

ルートを Router オブジェクトに直接登録することにより、ルートを HTTP ルーティングレイヤー に直接登録することもできます。起動時に Router インスタンスを取得するには、以下を実行します。

public void init(@Observes Router router) {
    router.get("/my-route").handler(rc -> rc.response().end("Hello from my route"));
}

ルート登録、オプション、および使用可能なハンドラーの詳細については、 Vert.x Web ドキュメント を確認してください。

Router アクセスは、quarkus-vertx-http エクステンションにより指定されます。quarkus-resteasy-reactive または quarkus-reactive-routes を使用すると、エクステンションが自動的に追加されます。

ルーターの Mutiny バリアント (io.vertx.mutiny.ext.web.Router) を受け取ることもできます。

public void init(@Observes io.vertx.mutiny.ext.web.Router router) {
    router.get("/my-route").handler(rc -> rc.response().endAndForget("Hello from my route"));
}

HTTP リクエストのインターセプト

また、受信した HTTP リクエストをインターセプトするようなフィルターを登録することも可能です。なお、これらのフィルタは、サーブレット、jakarta RESTリソース、リアクティブルートにも適用されます。

たとえば、次のコードスニペットは、HTTP ヘッダーを追加するフィルターを登録します。

package org.acme.reactive.routes;

import io.vertx.ext.web.RoutingContext;

public class MyFilters {

    @RouteFilter(100) (1)
    void myFilter(RoutingContext rc) {
       rc.response().putHeader("X-Header", "intercepting the request");
       rc.next(); (2)
    }
}
1 RouteFilter#value() は、フィルターの並べ替えに使用される優先度を定義します。優先度の高いフィルターが先に呼び出されます。
2 フィルターが `next()`メソッドを呼び出さなければ、チェーンを継続できない可能性があります。

HTTP 圧縮

HTTP レスポンスのボディは、デフォルトでは圧縮されていません。quarkus.http.enable-compression=true を使用して HTTP 圧縮サポートを有効にできます。

圧縮サポートが有効であれば、以下の場合にレスポンスボディが圧縮されます。

  • ルートメソッドが @ io.quarkus.vertx.http.Compressed でアノテーションされている、または

  • Content-Type ヘッダーが設定されており、その値は quarkus.http.compress-media-types を介して設定された圧縮メディアタイプである。

次の場合、レスポンスボディは圧縮されません。

  • ルートメソッドが @io.quarkus.vertx.http.Uncompressed でアノテーションされている、または

  • Content-Type ヘッダーが設定されていない。

デフォルトでは、次のメディアタイプのリストが圧縮されています: text/htmltext/plaintext/xmltext/csstext/javascriptapplication/javascript
クライアントが HTTP 圧縮をサポートしていない場合、レスポンスボディは圧縮されません。

OpenAPI と Swagger UI の追加

quarkus-smallrye-openapi エクステンションを使用してOpenAPI および Swagger UI のサポートを追加できます。

このコマンドを実行してエクステンションを追加

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

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

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

これだけで、Vert.x ルートから基本的な OpenAPI スキーマドキュメントを生成できます。

curl http://localhost:8080/q/openapi

生成された OpenAPI スキーマドキュメントが表示されます。

---
openapi: 3.0.3
info:
  title: Generated API
  version: "1.0"
paths:
  /greetings:
    get:
      responses:
        "204":
          description: No Content
  /hello:
    get:
      responses:
        "204":
          description: No Content
  /world:
    get:
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                type: string

OpenAPI ガイド も参照してください。

MicroProfile OpenAPI アノテーションの追加

MicroProfile OpenAPI を使用してスキーマをよりよくドキュメント化することができます。例えば、ヘッダー情報を追加したり、 void メソッドの戻り値の型を指定したりすると便利かもしれません:

@OpenAPIDefinition( (1)
    info = @Info(
        title="Greeting API",
        version = "1.0.1",
        contact = @Contact(
            name = "Greeting API Support",
            url = "http://exampleurl.com/contact",
            email = "techsupport@example.com"),
        license = @License(
            name = "Apache 2.0",
            url = "https://www.apache.org/licenses/LICENSE-2.0.html"))
)
@ApplicationScoped
public class MyDeclarativeRoutes {

    // neither path nor regex is set - match a path derived from the method name
    @Route(methods = Route.HttpMethod.GET)
    @APIResponse(responseCode="200",
            description="Say hello",
            content=@Content(mediaType="application/json", schema=@Schema(type=SchemaType.STRING))) (2)
    void hello(RoutingContext rc) {
        rc.response().end("hello");
    }

    @Route(path = "/world")
    String helloWorld() {
        return "Hello world!";
    }

    @Route(path = "/greetings", methods = HttpMethod.GET)
    @APIResponse(responseCode="200",
            description="Greeting",
            content=@Content(mediaType="application/json", schema=@Schema(type=SchemaType.STRING)))
    void greetings(RoutingExchange ex) {
        ex.ok("hello " + ex.getParam("name").orElse("world"));
    }
}
1 APIに関するヘッダー情報
2 レスポンスの定義

これにより、このOpenAPIスキーマが生成されます。

---
openapi: 3.0.3
info:
  title: Greeting API
  contact:
    name: Greeting API Support
    url: http://exampleurl.com/contact
    email: techsupport@example.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.1
paths:
  /greetings:
    get:
      responses:
        "200":
          description: Greeting
          content:
            application/json:
              schema:
                type: string
  /hello:
    get:
      responses:
        "200":
          description: Say hello
          content:
            application/json:
              schema:
                type: string
  /world:
    get:
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                type: string

Swagger UI の使用

Swagger UIは、 dev または test モードで実行するとデフォルトで含まれ、オプションで prod モードに追加することができます。詳細については、 Swagger UI ガイドを参照してください。

localhost:8080/q/swagger-ui/ を開いて、Swagger UI画面を確認します:

Swagger UI

まとめ

このガイドでは、Reactive Routesを使用してHTTPエンドポイントを定義する方法を紹介してきました。また、Quarkus HTTPレイヤーの構造とフィルターの書き方についても説明しました。