33

RESTのバージョン管理に関する多くの資料を読んだ後、APIの代わりに呼び出しのバージョン管理を考えています。例えば:

http://api.mydomain.com/callfoo/v2.0/param1/param2/param3
http://api.mydomain.com/verifyfoo/v1.0/param1/param2

最初に持っている代わりに

http://api.mydomain.com/v1.0/callfoo/param1/param2
http://api.mydomain.com/v1.0/verifyfoo/param1/param2

その後に行きます

http://api.mydomain.com/v2.0/callfoo/param1/param2/param3
http://api.mydomain.com/v2.0/verifyfoo/param1/param2

私が見る利点は次のとおりです。

  • 呼び出しが変更された場合、クライアント全体を書き直す必要はありません。変更された呼び出しの影響を受ける部分のみを書き直す必要があります。
  • クライアントの動作する部分はそのまま継続できます(クライアント側とサーバー側の両方が安定していることを確認するために、多くのテスト時間を費やしています)。
  • 変更された呼び出しには、永続的または非永続的なリダイレクトを使用できます。
  • 古いバージョンの呼び出しをそのままにしておくことができるので、下位互換性は簡単です。

私は何かが足りないのですか?お知らせ下さい。

4

6 に答える 6

64

HTTPヘッダーが必要です。

Version: 1

バージョンヘッダーはRFC4229に暫定的に登録されており、Xプレフィックスまたは使用法固有のURIの使用を避ける正当な理由がいくつかあります。より典型的なヘッダーは、 https : //stackoverflow.com/a/2028664でyfeldblumによって提案されました:

X-API-Version: 1

いずれの場合も、ヘッダーが欠落しているか、サーバーが提供できるものと一致しない場合は、失敗の理由とともに412PreconditionFailed応答コードを送信してください。これには、クライアントがサポートするバージョンを毎回指定する必要がありますが、クライアントとサーバー間で一貫した応答が強制されます。(オプションで?version=クエリパラメータをサポートすると、クライアントにさらに柔軟性が与えられます。)

このアプローチはシンプルで、実装が簡単で、標準に準拠しています。

代替案

非常に賢く、善意のある人々がURLのバージョン管理とコンテンツの交渉を提案していることを私は知っています。どちらも、特定のケースで、通常提案されている形で重大な問題を抱えています。

URLバージョン管理

エンドポイント/サービスのURLバージョン管理は、すべてのサーバーとクライアントを制御する場合に機能します。それ以外の場合は、古いサーバーにフォールバックする新しいクライアントを処理する必要があります。これは、制御外の異種サーバーにデプロイされたサーバーソフトウェアのシステム管理者があらゆる種類の作業を行う可能性があるため、カスタムHTTPヘッダーを使用することになります。302 Moved Temporarilyのようなものを使用すると、URLを簡単に解析できると思います。

コンテントネゴシエーション

ヘッダーを介したコンテンツネゴシエーションはAccept、HTTP標準に従うことを深く懸念しているが、HTTP/1.1標準ドキュメントが実際に述べていることを無視したい場合に機能します。あなたがよく目にする提案されたMIMEタイプは、ある種の形式application/vnd.example.v1+jsonです。いくつかの問題があります:

  1. もちろん、ベンダーの拡張機能が実際に適切である場合もありますが、クライアントとサーバー間のわずかに異なる通信動作は、新しい「メディアタイプ」の定義に実際には適合しません。また、RFC 2616(HTTP / 1.1)には、「メディアタイプの値はInternet AssignedNumberAuthorityに登録されています。メディアタイプの登録プロセスはRFC1590に概説されています。未登録のメディアタイプの使用はお勧めしません。」RESTAPIを備えたすべてのソフトウェア製品のバージョンごとに個別のメディアタイプを表示したくありません。
  2. サブタイプの範囲(たとえばapplication/*)は意味がありません。処理とフォーマットのために構造化データをクライアントに返すRESTAPIの場合、どのようなメリットがあり*/*ますか?
  3. Acceptヘッダーを正しく解析するには、ある程度の努力が必要です。実際にコンテンツネゴシエーションを正しく行うために必要なやりとりを最小限に抑えるために従う必要がある、暗黙的および明示的な優先順位の両方があります。この標準を正しく実装することに懸念がある場合、これを正しく行うことが重要です。
  4. RFC 2616(HTTP / 1.1)Acceptは、ヘッダーを含まないクライアントの動作について説明しています。 「Acceptヘッダーフィールドが存在しない場合、クライアントはすべてのメディアタイプを受け入れると見なされます。」したがって、自分で作成しないクライアント(制御が最も少ない場所)の場合、最も正しい方法は、サーバーが認識している最新の、最も壊れやすい古いバージョンを使用して要求に応答することです。約。つまり、バージョニングをまったく実装できなかったとしても、それらのクライアントはまったく同じように機能しなくなります。

編集、2014年

私は他の多くの答えとみんなの思慮深いコメントを読みました。数年のフィードバックの恩恵を受けて、これを改善できることを願っています。

  1. 'X-'プレフィックスは使用しないでください。私はおそらく2014年にもっと意味があると思います、そしてコメントで提起されたAccept-Version再利用のセマンティクスについていくつかの有効な懸念があります。Versionのような定義されたヘッダーとURIの相対的な不透明性は確かに重複しています。私は、この2つを、事実上ヘッダーContent-Versionであるコンテンツネゴシエーションのバリエーションと混同しないように注意しています。VersionURLの3番目の「バージョン」は、https://example.com/api/212315c2-668d-11e4-80c7-20c9d048772bデータまたはドキュメントのどちらが含まれているかに関係なく、「2番目のバージョン」とはまったく異なります。
  2. URLのバージョニング(たとえば、エンドポイントなど)について上記で述べたことに関してはhttps://example.com/v1/users、おそらくその逆が当てはまります。すべてのサーバーとクライアントを制御する場合は、URL/URIのバージョニングがおそらく必要です。単一のサービスURLを公開できる大規模なサービスの場合、ほとんどの場合と同様に、バージョンごとに異なるエンドポイントを使用します。私の特定の見解は、上記の実装が多くの異なる組織によって多くの異なるサーバーに、そしておそらく最も重要なことに、私が制御していないサーバーに最も一般的に展開されているという事実に大きく影響されます。常に正規のサービスURLが必要ですが、サイトでv3バージョンのAPIがまだ実行されている場合はhttps://example.com/v4/、Webサーバーの404NotFoundでリクエストが返されることを絶対に望んでいません。ページ(またはさらに悪いことに、200 OKで、ホームページをセルラーデータを介して500kのHTMLとしてiPhoneアプリに返します。)
  3. 非常に単純な/client/実装(およびより幅広い採用)が必要な場合、HTTP要求でカスタムヘッダーを要求することはGET、バニラURLを使用するのと同じくらい簡単であると主張するのは非常に困難です。(認証では、トークンまたはクレデンシャルをヘッダーで渡す必要がありますが、とにかく、実際のシークレットハンドシェイクと一緒にVersionまたはシークレットハンドシェイクとして使用することは非常に適しています。)Accept-Version
  4. ヘッダーを使用したコンテンツネゴシエーションAcceptは、同じコンテンツに対して異なるMIMEタイプを取得するのに適していますが(たとえば、XML対JSON対Adobe PDF)、それらのバージョンには定義されていません(DublinCore1.1対JSONP対PDF/A) 。業界標準を尊重することが重要であるためにヘッダーをサポートしたい場合Acceptは、要求で使用する必要がある可能性のあるメディアタイプネゴシエーションに干渉する作り上げのMIMEタイプは必要ありません。特注のAPIバージョンヘッダーは、頻繁に使用され、頻繁に引用されるものに干渉しないことが保証されていますAcceptが、それらを同じ使用法に統合すると、サーバーとクライアントの両方が混乱するだけです。そうは言っても、2013年のRFC6906に従って、名前空間に期待するものを名前空間に名前空間化する多くの理由から、個別のヘッダーよりも望ましいです。これはかなり賢いので、人々はこのアプローチを真剣に検討する必要があると思います
  5. すべてのリクエストにヘッダーを追加することは、ステートレスプロトコル内で機能することの1つの特定の欠点です。
  6. 悪意のあるプロキシサーバーは、HTTP要求と応答を破壊するためにほとんど何でもすることができます。すべきではありません。このコンテキストでCache-ControlヘッダーやVaryヘッダーについては説明しませんが、すべてのサービスクリエーターは、さまざまな環境でコンテンツがどのように消費されるかを慎重に検討する必要があります。
于 2012-08-12T16:20:14.813 に答える
17

これは意見の問題です。ここに私のものと、意見の背後にある動機があります。

  1. URLにバージョンを含めます。
    言う人のために、それはHTTPヘッダーに属している、と私は言います:多分。しかし、この分野の初期のリーダーによれば、URLを入力することはそれを行うための受け入れられた方法です。(グーグル、ヤフー、ツイッターなど)。これは開発者が期待することであり、開発者が期待することを実行すること、言い換えれば、驚き最小の原則に従って行動することは、おそらく良い考えです。それは絶対に「クライアントがアップグレードするのを難しくする」ことにはなりません。ここでの別の回答で示唆されているように、URLの変更が、消費するアプリケーションの開発者にとって何らかの障害となる場合は、その開発者を解雇する必要があります。

  2. マイナーバージョンをスキップする
    整数はたくさんあります。あなたは尽きるつもりはありません。そこに小数は必要ありません。APIの1.0から1.1への変更は、とにかく既存のクライアントを壊してはなりません。したがって、自然数を使用してください。分離を使用してより大きな変更を暗示する場合は、v100から開始してv200などを実行できますが、それでもYAGNIとそれはやり過ぎだと思います。

  3. バージョンをURIの左端に
    配置します。おそらく、モデルには複数のリソースがあります。それらはすべて同期してバージョン管理する必要があります。リソースXのv1とリソースYのv2を使用する人を配置することはできません。何かを壊してしまいます。バージョンを追加するときにメンテナンスの悪夢が発生することをサポートしようとすると、とにかく開発者にとって付加価値はありません。したがって、http://api.mydomain.com/v1/Resource/12345ここResourceで、はリソースのタイプであり12345、リソースIDに置き換えられます。

あなたは尋ねなかったが...

  1. URLパスから動詞を省略
    RESTはリソース指向です。URLパスに「CallFoo」のようなものがあります。これは不審に動詞のように見え、名詞とは異なります。これは間違っています。力ルークを使用。 RESTの一部である動詞を使用します:GET PUTPOSTDELETEなど。リソースの検証を取得する場合は、を実行しますGET http://domain/v1/Foo/12345/verification。更新する場合は、を実行してくださいPOST /v1/Foo/12345

  2. オプションのパラメータをクエリパラメータまたはペイロードとして配置するオプション
    のパラメータが自立型リソースを構成することを示唆している場合を除き、オプションのパラメータをURLパス(最初の疑問符の前)に配置しないでください。だから、POST /v1/Foo/12345?action=partialUpdate&param1=123&param2=abc

于 2012-11-24T23:25:34.183 に答える
4

バージョンをURI構造にプッシュするため、これらのいずれも実行しないでください。これにより、クライアントアプリケーションに欠点が生じます。アプリケーションの新機能を利用するためにアップグレードするのが難しくなります。

代わりに、URIではなくメディアタイプをバージョン管理する必要があります。これにより、最大限の柔軟性と進化能力が得られます。詳細については、私が別の質問に出したこの回答を参照してください。

于 2012-05-24T22:04:06.727 に答える
3

プロファイルメディアタイプパラメータを使用するのが好きです。

application/json; profile="http://www.myapp.com/schema/entity/v1"

より詳しい情報:

https://www.rfc-editor.org/rfc/rfc6906

http://buzzword.org.uk/2009/draft-inkster-profile-parameter-00.html

于 2014-03-23T09:12:05.650 に答える
1

APIでバージョンを呼び出す内容によって異なります。エンティティのさまざまな表現(xml、jsonなど)に対してバージョンを呼び出す場合は、acceptヘッダーまたはカスタムヘッダーを使用する必要があります。これが、httpが表現を操作するために設計された方法です。同じリソースを同時に呼び出して異なる表現を要求した場合、返されるエンティティはまったく同じ情報とプロパティ構造を持ちますが、形式が異なるため、この種のバージョン管理は表面的なものであるため、RESTfulです。

一方、「バージョン」をエンティティ構造の変更として理解している場合、たとえば、「ユーザー」エンティティに「年齢」フィールドを追加します。次に、リソースの観点からこれにアプローチする必要があります。これは、私の意見ではRESTfulアプローチです。Roy Fieldingが彼の論文で説明したように、RESTリソースは識別子からエンティティのセットへのマッピングです...したがって、エンティティの構造を変更するときは、それを指す適切なリソースが必要であることは理にかなっています。バージョン。この種のバージョン管理は構造的です。

私は次の場所で同様のコメントをしました:http://codebetter.com/howarddierking/2012/11/09/versioning-restful-services/

URLのバージョン管理を使用する場合、バージョンはURLの前ではなく、後になる必要があります。

GET/DELETE/PUT onlinemall.com/grocery-store/customer/v1/{id}
POST onlinemall.com/grocery-store/customer/v1

よりクリーンな方法でそれを行う別の方法ですが、実装時に問題が発生する可能性があります。

GET/DELETE/PUT onlinemall.com/grocery-store/customer.v1/{id}
POST onlinemall.com/grocery-store/customer.v1

このようにすることで、クライアントは、必要なエンティティにマップする必要なリソースを具体的に要求できます。実稼働環境で実装するときに本当に問題となるヘッダーやカスタムメディアタイプをいじる必要はありません。

また、URLをURLの後半に配置すると、クライアントは、メソッドレベルであっても、必要なリソースを具体的に選択するときに、より細かく設定できます。

しかし、開発者の観点から最も重要なことは、すべてのリソースとメソッドへのすべてのバージョンのマッピング(パス)全体を維持する必要がないことです。これは、サブリソース(埋め込みリソース)がたくさんある場合に非常に役立ちます。

実装の観点からは、リソースのレベルでそれを使用することは、たとえば、Jersey / JAX-RSを使用する場合、非常に簡単に実装できます。

@Path("/customer")
public class CustomerResource {
    ...
    @GET
    @Path("/v{version}/{id}")
    public IDto getCustomer(@PathParam("version") String version, @PathParam("id") String id) {
         return locateVersion(version, customerService.findCustomer(id));
    }
    ...
    @POST
    @Path("/v1")
    @Consumes(MediaType.APPLICATION_JSON)
    public IDto insertCustomerV1(CustomerV1Dto customer) {
         return customerService.createCustomer(customer);
    }

    @POST
    @Path("/v2")
    @Consumes(MediaType.APPLICATION_JSON)
    public IDto insertCustomerV2(CustomerV2Dto customer) {
         return customerService.createCustomer(customer);
    }
...
}

IDtoは、ポリモーフィックオブジェクトを返すための単なるインターフェイスであり、CustomerV1とCustomerV2はそのインターフェイスを実装します。

于 2014-11-06T22:21:19.913 に答える
0

FacebookはURLで検証を行います。現実の世界でも、URLのバージョン管理はよりクリーンで保守しやすいと感じています。

.Netを使用すると、次のようにバージョン管理を非常に簡単に行うことができます。

[HttpPost]
[Route("{version}/someCall/{id}")]
public HttpResponseMessage someCall(string version, int id))
于 2014-08-20T17:44:07.017 に答える