前文
RESTについて、なんとなくわかっているつもり。
でも、なんとなくイケてない気がする。REST ってこんなんだっけ?
と、なんちゃって REST な URL設計をするたびに迷いを抱えてやってきたのですが、
REST について基礎から調べ直したので、記事としてまとめました。
似たような状況の方々の助けになれば幸いです。
REST とは
システム設計の原則です。API に限らず通信するシステム全般に適用できる考え方です。
REpresentational State Transfer から四文字とってRESTだそうです。
元は論文の表現です。
脚注に論文へのリンクがありますので、気になる方は一度読んでみてはいかがでしょうか。1
REST の原則
原典である論文の REST の章は7つのセクションに分かれています。
そのうち、一般的な RESTful API を構築する上で重要になるのは以下の4つです。
- キャッシュ制御
- ステートレス
- 階層化システム
- 統一インターフェース
それぞれのメリット・デメリットについては調べれば良質な記事がいくらでも見つかるので、
この記事では差し控えます。
純粋に REST とは何ぞや? という点にフォーカスして解説していきます。
キャッシュ制御
REST はURL設計の規則ではなく、サーバーとクライアントの通信を定めた原則です。2
ですので、サーバー側だけでなく、クライアントの機能についても定められています。
クライアントは、レスポンスをキャッシュ可能です。
サーバーは、レスポンスがキャッシュ可能/不可能であることを
明示的、ないし暗黙的に提示しなければいけません。
例えば時間とともに変化し続けるリソースはキャッシュの対象として不適切でしょう。
反対に静的なリソースをキャッシュすれば、処理効率とユーザー体験の向上が見込めます。
実装の上では、Cache-Control
、Age
、Etag
、Vary
などを使って
制御することになります。
ステートレス
REST はステートレスです。つまり、状態を持ちません。
REST では、前回どんなやり取りをしたかを記憶していません。
前回のリクエスト/レスポンスと、今回のリクエスト/レスポンスは完全に独立しています。
「昨日読んだ本を、途中のページから再開する」 という機能は、
RESTful なシステムにはありません。前回の状態なんて覚えていないからです。
そういった情報はクライアント側、ブラウザやアプリで記憶しておきます。
RESTful なシステムへのリクエストは 「書籍Aの35ページ目」 のように
暗黙の文脈を持たずに、一つのやりとりで全ての情報を含んでおく必要があります。
実装の上では、サーバーセッションを使用せず、Cookie
などクライアント側に
情報を保存させるのが REST の原則です。
階層化システム
システムは自分の担当範囲を超えて「見る」ことができません。
分かりやすい例として、マイクロサービスの API をイメージすると近いでしょう。
API は固有の DB のみにアクセスし、それ以外の情報は API 同士で通信します。
担当範囲外の DB に直接接続して情報を参照することはありません。
統一インターフェース
RESTの中でも特に重要な、肝となるセクションです。
REST の統一インターフェースは、4つの要素で成り立ちますが、
REST の理解に必要なのは以下の2つです。
- リソースの識別
- 表現によるリソースの操作
順番に行きましょう。
リソースの識別
まず、「リソース」とはシステムで取り扱う概念、情報を指します。
システムで取り扱うものは、おおよそ全て「リソース」とすることが可能といえます。
論文には以下のようにあります。
文書や画像、一時的なサービス(例えば「今日のロサンゼルスの天気」)、他のリソースのコレクション、非仮想オブジェクト(例えば人)など、名前を付けることができるあらゆる情報がリソースになり得る。
そして、この「リソース」に一意な URI を割り当てることを、
「リソースの識別」といいます。
たとえば、「今日のロサンゼルスの天気」であれば
https://rest-weather.com/los-angeles/today
「7月17日のロサンゼルスの天気」であれば
https://rest-weather.com/los-angeles/2013/07/17
などの割り当てになります。
REST では「時間とともに変化するリソース」と、「ある時点でのリソースの情報」 は
同一の結果を返したとしても別の「リソース」として扱います。
つまり、今日が7月17日で、例示した2つのURLが同じ結果を返したとしても
それは別々な「リソース」であり「リソースに一意な識別子を割り当てる」ことに
違反していない、とするのが REST のスタイルです。
表現によるリソースの操作
REST ではリソースそのものではなく、リソースの表現によってリソースを操作します。
表現とは、乱暴に言ってしまえばリソースの JSON 表現だとか、
リソースの XML 表現だとか、そういったニュアンスで捉えて構いません。
この表現には、リクエストやレスポンスのヘッダ部分、つまり GET
、 POST
などの
HTTP メソッドや Authorization
などのヘッダ情報も含まれます。
ちなみに、原典では表現の例として、HTML ドキュメントや JPEG イメージも
挙げられています。リソースを表現さえできれば本当に何を使っても REST の内のようです。
統一インターフェースの残り2つの要素は? REST 原則の残り3つは?
RESTの理解に必要ないため、省略します。
どうしても気になる方は以下の折りたたみを御覧ください。
統一インターフェースの残り2つ
自己記述メッセージ
自己記述メッセージは、一言でいうとリクエストやレスポンスを説明するメタデータです。
歴史的な経緯としてHTTP通信は、メタデータの不備によって正しく解釈できない、エンコーディングを正しく解決できない時代があったそうです。
それを解決するために、REST は制約の中に解釈可能なメタデータを含むことを明示しているそうです。
HATEOAS
HATEOASは、RESTで実現されるナビゲーションです。
次にどのAPIにアクセスすべきか、というガイドをレスポンスに含めるのが、このHATEOASの決まりです。
HTTPページのリンクのように、REST でも次のページや機能へアクセスする便利な導線を提供することを目的としています。
あまり守られていませんね。
実際のところ原典の論文でも、この原則はオプショナル、つまり拡張機能のような位置づけにされています。
RESTの残り3つのセクション
- Null スタイルスタート
- クライアント・サーバー
- コードオンデマンド
以上です。一つずつ解説していきます。
Null スタイルスタート
乱暴に説明すると、「REST は0ベースで設計された」ということが記されています。
ここだけは詳しい解説が見つからず、自分で読んでみたのですが、「一般論として設計には2つの視点があり、RESTは後者の視点で開発された」とあるので、REST 原則についてというよりは、REST 原則に如何にしてたどり着いたか、の前提を説明しているセクションです。
クライアント・サーバー
REST はクライアントとサーバーに別れる、という規定です。
これは特別な概念とかではなく、普通に HTTP 通信でイメージされるクライアントとサーバーそのままです。
この原則によって、UIとデータストレージで、それぞれ関心事を分離します。
コードオンデマンド
アプレットやスクリプトをダウンロードして実行することで、クライアントの機能を拡張できるようにすること、と規定されています。
乱暴に説明すると、API を利用するクライアントは WEB からアップデートできるようにしておくこと、ということです。
分かりやすい例としては、API を利用するアプリがアプリストアから自動更新されたり、API を利用する SPA がサーバーから配信される最新の HTML/JS で動作したり、などの状態を指します。
クライアントのアップデート可能性も REST に規定されているんですね。
REST 以前に、WEB システムのあり方として当たり前に普及した考え方でしょう。
原典には、「デプロイ後に機能をダウンロードできるようにすることで、システムの拡張性が向上する。事前に実装する必要のある機能が減り、クライアントがシンプルになる」とあります。
URL設計してみる
ここまでで REST の原則論を一通りなぞることができました。
ここからは実際に URL を設計してみます。
お題としてmovie
を操作するURI、HTTPメソッドを定義してください、と
執筆者がオーダーを受けているので、movie
リソースを操作する
API を考えていきます。
URI | HTTP method | 説明 |
---|---|---|
https://rest-studio.com/movies |
GET | 映画一覧取得 |
https://rest-studio.com/movies |
POST | 映画登録 |
https://rest-studio.com/movies/1 |
GET | 映画詳細取得 |
https://rest-studio.com/movies/1 |
PATCH | 映画詳細変更 |
https://rest-studio.com/movies/1 |
DELETE | 映画削除 |
CURD です。基本ですね。一般に
「リソースは複数形にすること」
「URI に動詞を使わないこと」
等と言われています。
URI はあくまでリソースの識別に使い、操作は method など
表現の担当である、ということですね。
また、レスポンスにエンベロープを含めるべきではない、とも言われています。
リクエスト、レスポンスをできる限り簡素にし、ヘッダで表現できるものは
ヘッダで済ませたほうが良い、という考え方のためです。
URIにバージョンを含めるか
https://rest-studio.com/v1/movies
というURIはアリかナシか、という話ですね。
結論からいえば、よくある設計ですが最近はあまり好まれないようです。
というのも、URIに明示的にバージョンを含めてしまうと、クライアントがそのバージョンに縛られ依存してしまうためです。
そうなってしまうよりも、ヘッダにバージョンを記載して、クライアント側で問い合わせ先を柔軟に変えることができるようしておく方が好まれる傾向にあるように思います。
他にも、リクエストパラメータでバージョンを指定する方法もありますが、これはめったに見なくなりました。
URI | HTTP method | 説明 |
---|---|---|
https://rest-studio.com/movies/playing |
GET | 上映中映画一覧 |
https://rest-studio.com/movies/playing?location=tokyo |
GET | 東京で上映中映画一覧 |
https://rest-studio.com/movies/1/actors |
GET | 出演者一覧 |
https://rest-studio.com/movies/1/actors/1 |
GET | 出演者 |
1つ目の例は今日の天気と似た構造ですね。時間とともに変化するリソースです。
2つ目はGETパラメータがついています。検索項目のような任意パラメータは
URIに含めない形にすると収まりが良いです。
3つ目、4つ目はサブリソースです。特定の映画の子リソースとしてアクセス可能になっています。
フォーマットをどうやって指定するか
例えばJOSNレスポンスを期待しているとき、サーバーにそれを伝える方法は3つあります。
- リクエストパラメータ
- URI拡張子
- Acceptヘッダ
一般的にはAccept
ヘッダが好ましいとされるようです。
URIはリソースを表すため、その表現はURIでない場所にするべき、ということですね。
URI | HTTP method | 説明 |
---|---|---|
https://rest-studio.com/movies/1/actors/1 |
PATCH | 出演者情報変更 |
https://rest-studio.com/movies/1/actors/1 |
PUT | 出演者変更 |
どちらもサブリソースの変更です。しかし、メソッドが違いますね。
これはPATCH
とPUT
の用途の差異からくる違いです。
PUT と PATCH の使い分け
どちらも変更として使用するメソッドですが、明確に使い分けがあります。
まず、一般的な変更はPATCH
です。情報の一部分だけ書き換える変更ですね。
では、PUT
はどのように使うのかといえば、上書きです。
置換(replace)といったほうが通りがいいかもしれません。
とにかくリソースをまるっと差し替えるときはPUT
を使用します。
ところで、API処理の分類には2つの観点があります。
安全性とべき等性です。
安全性とべき等性
安全性とは副作用がないことです。言い換えればデータを書き換えない処理、READのことです。
べき等性は何度その処理を実行しても同じ結果になることです。
表に表すと以下のようになります。
安全 | 安全でない | |
---|---|---|
べき等 | GET | PUT、PATCH、DELETE |
べき等でない | POST |
副作用があってもべき等なのか?
変更を加える処理なのに何度実行しても同じ結果になるということはあるのでしょうか。
結論から言えばあります。
例えば、PATCH
で変更を加えた後、リロードしてもう一度同じ処理を実行するとどうなるでしょうか。
一度目の変更と全く同じ変更が実施されるため、結果として何も変更されません。何度実行しても同じことです。
DELETE
も同様で、一度削除したリソースに対してもう一度削除を実施しても、もはや削除するリソースが存在しないため何も変更は起きません。
これはべき等の特徴です。
べき等でないPOST
はどうなのかといえば、新しくリソースを登録する処理を何度もリロードした場合、
同じ内容のリソースがリロードした回数だけ作成されます。
これはべき等ではありません。
最後に
REST について表面的な設計手法ではなく、原典の論文から原則について触れてきました。
しかし、原典に忠実に作ることが必ずしも正しいわけではありません。
RESTは設計思想であり、規約ではないからです。
REST成熟レベルという分類もあるため、更に詳しく知りたい方は調べてみるとよいかと思います。
皆さんのREST設計が少しでも楽になることを願っています。
-
CHAPTER 5: Representational State Transfer (REST) - Architectural Styles and the Design of Network-based Software Architectures ↩
-
論文内では、「constraint」、制約と表現されています。 ↩