Quarkusでのパス解決
ここ数週間、私たちは通常になく揺れ動いていました。パスの解決はこっそりと複雑になることがあり、物事を良くしようとしているうちに、私たちは誤って悪化させてしまったのです!今はすべて修正しましたが、いくつかの変更点があることに気づくかもしれません。うまくいけば、この記事でその変更が何であるか、その意味、そしてあなたが望むようにすべてを元の状態に戻すために何ができるか明確になるでしょう。
TL;DR: 1.11.5.Final
と 1.12.1.Final
の時点では、設定における先頭のスラッシュは重要です。つまり、 /endpoint
を使用した場合、そのエンドポイントは絶対ルートから提供されます。もし、包含するバケットからの相対的なものにしたいのであれば、先頭のスラッシュを省略してください。例えば、 quarkus.http.non-application-root-path
はデフォルトでは q
になり、 quarkus.http.root-path
の下に入れ子になり、元の動作と一致します。アプリケーション以外のエンドポイントを絶対ルートから (設定されている場合は http ルートの兄弟として) 提供するために、例えば /q
のような絶対パスを使うことができます。アプリケーション以外のエンドポイントの動作を完全に削除するには、 quarkus.http.non-application-root-path
を quarkus.http.root-path
と同じ値に設定します。これを行う最も簡単な方法は、変数を使用することです: quarkus.http.non-application-root-path=${quarkus.http.root-path}
.
長文での説明
昔々、Quarkusでは、ヘルスチェックやメトリクスなどのエンドポイントを追加で定義していました。これらは、アプリケーションが定義したエンドポイントと一緒に quarkus.http.root-path
から提供されていました。デフォルトでは quarkus.http.root-path
は /
であり、事実上見えないようになっているため、これは必ずしも明らかではありません。
そして、 アプリケーションエンドポイントとの衝突を避け、セキュリティやアクセスポリシーの処理を容易にするために、アプリケーション以外のエンドポイントをどのようにグループ化するかを考え始めました。さらに何人かのユーザからは、これらの非アプリケーションエンドポイントを 完全に別のポートから提供できないかという意見もありました (まだ実現していません)。
最初のステップは、これらのエクステンションで定義されたエンドポイントをすべて一緒にグループ化することでした。これが、 非アプリケーションエンドポイントパスの始まりでした。この新しいパスのデフォルトの場所は /q
で、他のエンドポイントと同様に HTTP ルートパスの下に入れ子になっていました。この効果は、例えば /health
を /q/health
に移動させることでした。
私たちは、メトリクスやヘルスなどのエンドポイントの一部を移動することは、すでに展開されているアプリケーションや人間の記憶にとって問題があることを知っていました。移行を容易にするために、これらのエンドポイントの一部にリダイレクトを追加し、 /metrics
にアクセスした場合は /q/metrics
にリダイレクトされるようにしました。
アプリケーション以外のエンドポイントのサポートは 1.11.0.Final
で出荷されました。
そして、事態は 悪化 し始めました。クラウドホスティングプロバイダの中には、例えば、ヘルスの定義として 200
のみを受け入れるものがあり、リダイレクト ( 301
) は意図した効果を発揮しませんでした。また、アプリケーション以外のエンドポイントをオフにして以前の動作に戻す方法についても混乱があり、特定のエンドポイントをこのアプリケーション以外のエンドポイントコレクションから移動させる方法についても疑問が残りました。
余談ですが、どのようにしてこのような状況になったかというと、ライブラリの挙動の違いによるものではありません。Vert.x は、例えばルートを作成する際に、常に先頭のスラッシュで始まるセグメントを求めますが、JAX-RS は @Path
のアノテーションでは先頭のスラッシュを事実上無視します。Vert.x に慣れている人は常に先頭のスラッシュを追加し、JAX-RS を使用している人は何をしても魔法のように動作します。
Quarkusでは、実装の詳細が偶然にも露わになりました。エクステンションによって定義されたアプリケーション以外のエンドポイントは、Vert.xのルートをベースにしています。デフォルトのパス設定値はスラッシュで始まり、迅速なルート作成を可能にし、単純な追加動作を可能にします。Quarkusの初期の頃は、これが悪い考えであることを示唆するようなものは何もなく、JAX-RSの経験がある開発者は、JAX-RSが処理するので、どちらにしても警告を受けることはありませんでした。
しかし、人々が期待していた通りにパスが解決されず、その状況を解決するために必要な設定の変更が直感的ではなかったり、別の問題を引き起こしたりする状況に陥りました。そこで私たちは、異なる設定値を組み合わせたときに何が起こるかを並べて見ることができるように、すべての可能な設定の組み合わせをスプレッドシートに入れてみました。結果はそれほど素晴らしいものではありませんでした。しかし、この演習により、アプリケーションと非アプリケーションのエンドポイントが必要な動作をするようにするために何を変更する必要があるかを評価するために、一歩下がって全体像を見ることができました。
Quarkusでパスを設定するために使用される設定属性のセットは変更されませんが、設定された値がどのように解釈されるかは異なります。
-
エンドポイントのパス設定のデフォルト値が相対値になりました。
/q
はq
に、/metrics
はmetrics
になりました。これは、設定変更なしで、これらのエンドポイントが含まれているルートに対して相対的に解決されることを意味します。これは、JAX-RSが仕様ごとに行うことであり、ほとんどのユーザーが直感的に期待していることです。 -
明示的に設定された値の先頭のスラッシュは重要です。 エンドポイントを特定の場所に移動させたいと考えている人がいることは承知しており、その意図を表現する最も一貫した方法は、エンドポイントに使用させたい URI を正確に指定できるようにすることです。
/metrics
を指定すると、それがメトリクス・エンドポイントを見つける場所になります。
1.11.5.Final
と 1.12.1.Final
でこれらのアップデートが利用可能になり、埃は全て解消するはずです。
アプリケーション以外のエンドポイントのための便利なリダイレクトはまだ存在しますが、 quarkus.http.redirect-to-non-application-root-path
を false
に設定することで無効にすることができます。これは全く変わっていません。
設定されたパスの解決
新しいルールを使用してパスがどのように解決されるか、いくつかの例を見てみましょう。まず、以下の前提条件から始めましょう。
-
@ApplicationPath("/hello")
という定義のあるHello World アプリケーションがあるとします。 -
アプリケーションは、
@Path("world")
と@Path("/aliens")
のある2つのエンドポイントを指定します。
私たちが最も気にしている設定属性は:
-
quarkus.http.root-path
- HTTP ルートパス。すべてのウェブコンテンツはこのルートパスからの相対パスで提供されます。 -
quarkus.http.non-application-root-path
- アプリケーション以外のエンドポイントのルートパス。
また、設定可能なアプリケーション以外のエンドポイントにも注目しています:
-
quarkus.micrometer.export.prometheus.path
- Micrometerメトリクスのエンドポイントの場所。 -
quarkus.smallrye-health.root-path
- すべてを網羅したヘルスエンドポイントの場所。 -
quarkus.smallrye-health.liveness-path
- liveness エンドポイントの場所。
レバーを引き始めると何が起こるかを見てみましょう。以下の例では、config の句読点に注意を払ってください。句読点が、そのように動作する鍵となるからです。
デフォルト
デフォルトの設定値は以下の通りです。
-
quarkus.http.root-path=/
-
quarkus.http.non-application-root-path=q
-
quarkus.micrometer.export.prometheus.path=metrics
-
quarkus.smallrye-health.root-path=health
-
quarkus.smallrye-health.liveness-path=liveness
(宣言されたアプリケーションエンドポイントと組み合わされた)この設定は、Quarkusアプリケーションが開発モードで実行されている場合、次のような有効なURLになります。
この例では quarkus.http.root-path
が隠れていることに注意してください。値が /
だからです。
この場合、 quarkus.http.non-application-root-path
と quarkus.http.root-path
は同じではないので、利便性の為のリダイレクトがあります。この設定では、 /metrics
は /q/metrics
にリダイレクトされます。
Http ルートパスの変更
HTTP のルートパスを /root
に変更して、リソース解決への影響が見えるようにしてみましょう。
-
quarkus.http.root-path=/root
-
quarkus.http.non-application-root-path=q
-
quarkus.micrometer.export.prometheus.path=metrics
-
quarkus.smallrye-health.root-path=health
-
quarkus.smallrye-health.liveness-path=liveness
その結果、以下のような開発モードのURLになります。
この場合も、 quarkus.http.non-application-root-path
と quarkus.http.root-path
.は同じではないので、便利なリダイレクトがあります。この設定では、 /root/metrics
は /root/q/metrics
にリダイレクトされます。これは、アプリケーション以外のエンドポイントが暗黙のうちに HTTP ルートパスから相対化されていた以前の動作と一致しています。
アプリケーション以外のルートパスを移動 (/q)
今までできなかったことをやってみましょう。絶対パス /q
を指定して、アプリケーション以外のエンドポイントをHTTPルートパスの外側に移動させます。
-
quarkus.http.root-path=/root
-
quarkus.http.non-application-root-path=/q
-
quarkus.micrometer.export.prometheus.path=metrics
-
quarkus.smallrye-health.root-path=health
-
quarkus.smallrye-health.liveness-path=liveness
その結果、以下のような開発モードのURLになります。
この場合も、 quarkus.http.non-application-root-path
と quarkus.http.root-path
が同一ではない為、利便性の為のリダイレクトが存在します。リダイレクトされた URL はまだ HTTP ルートに対する相対的なものなので、 /root/metrics
は /q/metrics
にリダイレクトされます。
アプリケーション以外のエンドポイントを個別に移動 (/metrics と /liveness)
これは、以前は不可能だったもう一つの設定です。設定可能なアプリケーション以外のエンドポイントを、指定された絶対パスに個別に移動させることができます。特にこの例では /metrics
と /liveness
に移動させることが出来ます:
-
quarkus.http.root-path=/root
-
quarkus.http.non-application-root-path=/q
-
quarkus.micrometer.export.prometheus.path=/metrics
-
quarkus.smallrye-health.root-path=health
-
quarkus.smallrye-health.liveness-path=/liveness
その結果、以下のような開発モードのURLになります。
quarkus.http.non-application-root-path
は quarkus.http.root-path
と同じではないので、この場合も利便性の為のリダイレクトがあります。しかし、これらのリダイレクトは、非アプリケーションエンドポイントのルートによって制御される非アプリケーションエンドポイントにのみ適用されます。私たちは、基本的にそのルートからメトリクスとlivenessのエンドポイントを削除しているので、リダイレクトされることはありません。この設定では、 /root/health
をリクエストすると /q/health
にリダイレクトされます。 /root/health/liveness
や /root/metrics
にはリダイレクトされません。
非アプリケーションエンドポイントのルートの削除
何人かの方から、このアプリケーション以外のエンドポイントルートのものを完全にオフにするにはどうすればよいかという質問を受けました。あなたの意図を明確に表現するのがベストです。非アプリケーションエンドポイントを無効にするには、HTTPルートパスと同じにしてください。要するに、ランタイムに「HTTPルートからすべての非アプリケーションエンドポイントを提供する」ように指示していることになります。この例では、値が同じであることを保証するために変数を使用しています。
-
quarkus.http.root-path=/root
-
quarkus.http.non-application-root-path=${quarkus.http.root-path}
-
quarkus.micrometer.export.prometheus.path=metrics
-
quarkus.smallrye-health.root-path=health
-
quarkus.smallrye-health.liveness-path=liveness
その結果、以下のような開発モードのURLになります。
このシナリオでは、アプリケーション以外のエンドポイントの動作が完全に無効化されているため、利便性のためのリダイレクトはありません。
ノックオン効果
ほとんどの部分では、これが透明であることを願っています。途中で非常に一貫性のないパス処理を発見し、これらの値の多く(あるいはほとんど)がカスタマイズされることはないと考えさせられました。
HTTP ルートパスをカスタマイズしている場合は、挙動の変化に気づく可能性が高いでしょう。その場合は、上記の新しいルールと例が、設定を微調整してすべてを思い通りに動作させる方法を理解するのに役立つことを願っています。
エクステンション開発者は最も大きな変更点を目にすることになるでしょう。 エクステンション開発ガイドが更新され、アプリケーション以外のエンドポイントを作成するために使用されるビルド項目の変更が記述されています。しかし、一般的なルールとしては、自分でエンドポイントパスを構築することは避け、 NonApplicationRootPathBuildItem
と HttpRootPathBuildItem
に頼って構築してもらうようにしましょう。