予測される変更を計画しているRESTエンドポイントの推奨パターンは何ですか


25

変更のための先見の明を持つ外部アプリケーション用のAPIを設計しようとすることは簡単ではありませんが、少し前もって考えておくことで、後で生活が楽になります。私は、以前のバージョンのハンドラーをそのままにしておくことで、後方互換性を維持しながら将来の変更をサポートするスキームを確立しようとしています。

この記事の主な関心事は、特定の製品/会社に対して定義されたすべてのエンドポイントでどのパターンに従う必要があるかです。

基本スキーム

のベースURLテンプレートを考えると、https://rest.product.com/すべてのサービスが/api/authおよびなどの他の非レストベースのエンドポイントと共に存在することを考案しました/doc。したがって、次のようにベースエンドポイントを確立できます。

https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...

サービスエンドポイント

次に、エンドポイント自体について説明します。懸念はPOSTGETDELETEこの記事の主な目的ではなく、それらのアクション自体に懸念されます。

エンドポイントは、名前空間とアクションに分類できます。また、各アクションは、戻り値の型または必須パラメーターの基本的な変更をサポートする方法で表示される必要があります。

登録されたユーザーがメッセージを送信できる架空のチャットサービスを利用すると、次のエンドポイントがある場合があります。

https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send

今度は、壊れる可能性のある将来のAPI変更に対するバージョンサポートを追加します。私たちは、どちらかの後にバージョンの署名を追加することができ/api/たりした後/messages/。与えられたsendエンドポイント我々は、v1の以下のものを持つことができます。

https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send

だから私の最初の質問は、バージョン識別子の推奨場所は何ですか?

コントローラーコードの管理

そのため、以前のバージョンをサポートする必要があることを確立しました。そのため、時間の経過とともに廃止される可能性のある新しいバージョンのそれぞれのコードを何らかの方法で処理する必要があります。Javaでエンドポイントを記述していると仮定すると、これをパッケージで管理できます。

package com.product.messages.v1;
public interface MessageController {
    void send();
    Message[] list();
}

これには、すべてのコードがネームスペースを介して分離されているという利点があります。この場合、重大な変更はサービスエンドポイントの新しいコピーを意味します。これの不利な点は、すべてのコードをコピーする必要があり、新しいバージョンと以前のバージョンに適用するバグ修正を各コピーに適用/テストする必要があることです。

別のアプローチは、エンドポイントごとにハンドラーを作成することです。

package com.product.messages;
public class MessageServiceImpl {
    public void send(String version) {
        getMessageSender(version).send();
    }
    // Assume we have a List of senders in order of newest to oldest.
    private MessageSender getMessageSender(String version) {
        for (MessageSender s : senders) {
            if (s.supportsVersion(version)) {
                return s;
            }
        }
    }
}

これにより、バージョン管理が各エンドポイント自体に分離され、ほとんどの場合一度適用するだけでバグ修正とポート互換性が保たれますが、これをサポートするには個々のエンドポイントにもう少し作業を行う必要があります。

そこで、2つ目の質問「RESTサービスコードを設計して以前のバージョンをサポートするための最良の方法は何か」があります。

回答:


13

そこで、2つ目の質問「RESTサービスコードを設計して以前のバージョンをサポートするための最良の方法は何か」があります。

非常に慎重に設計された直交APIは、おそらく後方互換性のない方法で変更する必要はないので、本当に最善の方法は将来のバージョンを持たないことです。

もちろん、最初の試行ではおそらくそれを実際に得られないでしょう。そう:

  • 計画しているとおりにAPIをバージョン管理し(バージョン管理されているのはAPIであり、個々のメソッドではありません)、それについて多くの騒ぎを起こします。APIが変更される可能性があることをパートナーに知らせ、アプリケーションが最新バージョンを使用しているかどうかを確認する必要があることを確認してください。新しいものが利用可能になったらアップグレードするようユーザーにアドバイスします。2つの古いバージョンをサポートするのは困難ですが、5つをサポートすることは受け入れられません。
  • 「リリース」ごとにAPIバージョンを更新する衝動に抵抗します。通常、既存のクライアントを壊すことなく、新しい機能を現在のバージョンにロールバックできます。それらは新しい機能です。最後にしたいことは、クライアントがバージョン番号を無視することです。バージョン番号はとにかく下位互換性があるためです。既存のAPIを破壊せずに前進できない場合にのみ、APIバージョンを更新してください。
  • 新しいバージョンを作成するときが来たら、最初のクライアントは以前のバージョンの後方互換性のある実装でなければなりません。「メンテナンスAPI」自体は、現在のAPIに実装する必要があります。このように、いくつかの完全な実装を維持するためのフックにはありません。現在のバージョンのみ、および古いバージョンのいくつかの「シェル」。後方互換性のあるクライアントに対して廃止されたAPIの回帰テストを実行することは、新しいAPIと互換性レイヤーの両方をテストする良い方法です。

3

最初のURI設計オプションは、API全体をバージョン管理しているという考えをよりよく表しています。2番目は、メッセージ自体のバージョン管理と解釈できます。だから、これはより良いIMOです:

rest.product.com/api/v1/messages/send

クライアントライブラリの場合、別々のJavaパッケージで2つの完全な実装を使用する方が、クリーンで使いやすく、保守しやすいと思います。

そうは言っても、バージョニングよりもAPIを進化させるための優れた方法があります。私はあなたがそれに備えなければならないことに同意しますが、私はバージョン管理を最後の手段として考え、慎重に控えめに使用することです。クライアントがアップグレードするのは比較的大きな努力です。しばらく前に、これらの考えのいくつかをブログ記事に掲載しました。

http://theamiableapi.com/2011/10/18/api-design-best-practice-plan-for-evolution/

特にREST APIのバージョン管理については、Mark Nottinghamによるこの投稿が役立ちます。

http://www.mnot.net/blog/2011/10/25/web_api_versioning_smackdown


3

APIバージョン管理を処理する別のアプローチは、HTTPヘッダーでバージョンを使用することです。いいね

POST /messages/list/{user} HTTP/1.1
Host: http://rest.service.com
Content-Type: application/json
API-Version: 1.0      <----- like here
Cache-Control: no-cache

ヘッダーを解析し、バックエンドで適切に処理できます。

このアプローチでは、クライアントはURLを変更する必要はなく、ヘッダーだけを変更する必要があります。また、これにより、RESTエンドポイントが常にクリーンになります。

クライアントのいずれかがバージョンヘッダーを送信しなかった場合は、400-Bad requestを送信するか、APIの下位互換バージョンで処理できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.