変更に対する先見性を備えた外部アプリケーション用の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/...
次に、エンドポイント自体について説明します。 POST
、GET
、DELETE
に関する懸念は、この記事の主な目的ではなく、それらのアクション自体に対する懸念です。
エンドポイントは名前空間とアクションに分解できます。各アクションは、戻り値の型または必須パラメーターの根本的な変更をサポートする方法で表示される必要もあります。
登録ユーザーがメッセージを送信できる架空のチャットサービスを利用して、次のエンドポイントを設定できます。
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サービスコードを設計する最良の方法は何ですか?」
そこで、2つ目の質問があります。「以前のバージョンをサポートするためのRESTサービスコードを設計する最良の方法は何ですか?」
非常に注意深く設計されており、直交APIはおそらく下位互換性のない方法で変更する必要がないため、本当に最善の方法は将来のバージョンを持たないことです。
もちろん、おそらく最初の試みは実際には得られないでしょう。そう:
最初のURI設計オプションは、API全体をバージョン管理するという考えをよりよく表現しています。 2番目は、メッセージ自体のバージョン管理と解釈できます。したがって、これはIMOの方が優れています。
rest.product.com/api/v1/messages/send
クライアントライブラリの場合、2つの完全な実装を別々のJavaパッケージで使用する方が、クリーンで使いやすく、保守も簡単だと思います。
とはいえ、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
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を送信するか、backward-compatible APIのバージョンで処理できます。