Spring 3.2.x を使用して REST API バージョンを管理する方法を探していましたが、メンテナンスが簡単なものは見つかりませんでした。まず、私が抱えている問題について説明し、次に解決策を説明しますが、ここで車輪の再発明をしているのではないかと疑問に思っています。
Accept ヘッダーに基づいてバージョンを管理したいのですが、たとえば、リクエストに Accept ヘッダーがある場合application/vnd.company.app-1.1+json
、Spring MVC がこれをこのバージョンを処理するメソッドに転送するようにします。また、API 内のすべてのメソッドが同じリリースで変更されるわけではないため、各コントローラーにアクセスして、バージョン間で変更されていないハンドラーに対して何かを変更することはしたくありません。また、Spring はどのメソッドを呼び出すかすでに検出しているため、コントローラー自体でどのバージョンを使用するかを判断するロジック (サービス ロケーターを使用) も必要ありません。
したがって、バージョン 1.0 から 1.8 までの API で、バージョン 1.0 でハンドラーが導入され、バージョン 1.7 で変更された場合、これを次のように処理します。コードがコントローラー内にあり、ヘッダーからバージョンを抽出できるコードがあるとします。(以下は Spring では無効です)
@RequestMapping(...)
@VersionRange(1.0,1.6)
@ResponseBody
public Object method1() {
// so something
return object;
}
@RequestMapping(...) //same Request mapping annotation
@VersionRange(1.7)
@ResponseBody
public Object method2() {
// so something
return object;
}
これは Spring では不可能です。2 つのメソッドに同じRequestMapping
アノテーションがあり、Spring がロードに失敗するためです。アノテーションでオープンまたはクローズ バージョン範囲を定義できるという考え方ですVersionRange
。最初のメソッドはバージョン 1.0 から 1.6 まで有効ですが、2 番目のメソッドはバージョン 1.7 以降 (最新バージョン 1.8 を含む) で有効です。誰かがバージョン 99.99 を渡すとこのアプローチが機能しなくなることはわかっていますが、私はそれで問題ありません。
さて、上記のことはSpringの動作を大幅に変更しなければ不可能なので、ハンドラがリクエストに一致する方法をいじって、特に独自の を書いて、そこにバージョン範囲を含めることを考えていましProducesRequestCondition
た。例えば、
コード:
@RequestMapping(..., produces = "application/vnd.company.app-[1.0-1.6]+json)
@ResponseBody
public Object method1() {
// so something
return object;
}
@RequestMapping(..., produces = "application/vnd.company.app-[1.7-]+json)
@ResponseBody
public Object method2() {
// so something
return object;
}
この方法では、アノテーションの produces 部分で、閉じたバージョン範囲または開いたバージョン範囲を定義できます。現在、このソリューションに取り組んでいますが、いくつかのコア Spring MVC クラス ( RequestMappingInfoHandlerMapping
、RequestMappingHandlerMapping
およびRequestMappingInfo
) を置き換える必要があるという問題があります。これは、Spring の新しいバージョンにアップグレードするたびに余分な作業が必要になるため、好ましくありません。
ご意見をお聞かせいただければ幸いです。特に、これをよりシンプルで保守しやすい方法で行うための提案があれば、ぜひお聞かせください。
編集
懸賞金を追加します。懸賞金を獲得するには、コントローラー自体にこのロジックを含めることを提案せずに、上記の質問に答えてください。Spring にはすでに、呼び出すコントローラー メソッドを選択するためのロジックが多数用意されており、これに便乗したいと考えています。
編集2
オリジナルの POC (いくつかの改良を加えたもの) を github で共有しました:https://github.com/augusto/restVersioning
ベストアンサー1
下位互換性のある変更を行うことでバージョン管理を回避できるかどうかにかかわらず (企業のガイドラインに縛られている場合や、API クライアントがバグのある方法で実装されていて、本来であれば問題がなくても問題が発生する場合は、必ずしも可能とは限りません)、抽象化された要件は興味深いものです。
メソッド本体で評価を行わずに、リクエストからのヘッダー値の任意の評価を行うカスタム リクエスト マッピングを実行するにはどうすればよいですか?
記載の通りこのSOの答え実際には、同じものを使用して@RequestMapping
、実行時に発生する実際のルーティング中に異なるアノテーションを使用して区別することができます。そのためには、次の操作を行う必要があります。
- 新しい注釈を作成します
VersionRange
。 - を実装します
RequestCondition<VersionRange>
。ベストマッチ アルゴリズムのようなものが存在するため、他のVersionRange
値で注釈が付けられたメソッドが現在のリクエストに対してより適切な一致を提供しているかどうかを確認する必要があります。 - アノテーションとリクエスト条件に基づいて実装します
VersionRangeRequestMappingHandlerMapping
(投稿で説明されているように)@RequestMappingカスタムプロパティを実装する方法)。 VersionRangeRequestMappingHandlerMapping
デフォルトを使用する前に、Spring が を評価するように設定しますRequestMappingHandlerMapping
(たとえば、順序を 0 に設定します)。
これには、Spring コンポーネントのハッキング的な置き換えは必要ありませんが、Spring の構成と拡張メカニズムを使用するため、Spring バージョンを更新した場合でも機能するはずです (新しいバージョンがこれらのメカニズムをサポートしている限り)。