リソースとは何か
Web上に存在する情報、データのこと。コンピュータ上に格納することができ、一連のビットで表現できる。また、それ自体を参照するに値するほどの重要性を持っている。例えば、ドキュメント、データベースの行等。リソースは名詞で定義される。
リソースの例としては以下のようなものがある。
- ソフトウェアリリースのバージョン(1.0.3等)
- 猫に関する情報
- 天気予報
- 1024の次の素数
「Web APIとは何なのか」でも述べた通り、それらが必要であれば公開したほうが良い。
リソース指向アーキテクチャ(ROA)とは何なのか
リソース指向アーキテクチャ(Resource Oriented Architecture、以下ROA)とは、それ自体を参照するに値するものを「リソース」として定義し、リソースを中心に考えるアーキテクチャのこと。リソースという概念、リソースはURIを持つ(URI)、リソースに対する操作(統一インターフェース)、リソースの表現形式(表現)、というようにリソース指向の中心はあくまでリソースになる。また、ROAはRESTful APIを実装するうえで問題を解決してくれるアーキテクチャでもある。言い換えるとRESTful APIはROAの実装ということになる(ROAに則りRESTful APIは実装されている)。ROAはRESTful Webサービスで初めて提唱された。
※ちなみに、「REST」という言葉はアーキテクチャではなく一連の設計条件のことで、RESTアーキテクチャというものは存在しない。
詳細は後述するが、ROAには以下の概念と特性が存在する。
ROAの4つの概念
ROAの4つの特性
- アドレス可能性(アドレス可能性)
- ステートレス性(ステートレス性)
- 接続性(リンクと接続性)
- 統一インターフェース(統一インターフェース)
URI
リソースは少なくとも1つのURIを持っていなければならない(リソースはURIを通じて提供される)。URIはリソースの名前とアドレスである。URIを持たない情報はリソースではなく、他のリソースを説明するデータとして以外に、Web上に存在することはない。例えば、広告にURIを掲載し、その広告を見た人がブラウザにそのURIを打ち込むと、広告主が表示したいと考えていたリソースが表示される。このようにリソースはURIを持っていなければならない。
URIは記述的であるべき
URIとリソースは直感的に対応させたほうが良い。さらに、URIは構造的でかつ、その構造は予測可能な方法で区別さることが好ましい。
例えば、猫の情報について調べるため、
/search/cat
にアクセスし、ネズミについて調べるため、
/i-want-to-know-about/Mice
にアクセスさせるようなことは避けるべきである。
URIを必要としているクライアントからすると、構造的で予測可能なほうがリソースを扱いやすく、また、この考え方は、優れたWebの設計ルールの1つにもなっている。そのため、上記例のネズミについて調べる場合、
/search/mouse
とさせるべきである。
URIとリソースの関係
定義上、2つのリソースが同じであることはありえない。それらが同じであるならば、リソースは1つしか存在しないはずである。ただし、何らかの時点で、2つの異なるリソースが同じデータをポイントすることがある。
例えば、以下の2つはしばらくの間は同じファイルを指している。
/software/releases/1.0.3.zip
/software/releases/latest.zip
だが、これらの2つのURIの目的は異なっている。上の方は常に特定のバージョンをポイントし、下の方はクライアントがアクセスした時点での最新のバージョンをポイントしている。この場合、これは2つの概念で、2つのリソースであるため問題はない。
リソースはURIを1つ以上持つことができるため、本当に同じリソースに2つ以上のURIが結びつくこともある。こういったケースの場合、結びつくURIが増えるたびにURIの価値が希薄化してしまう。これを回避する方法の1つとして、同じリソースに対して複数のURIをサポートし、そのうち1つをそのリソースの正規URIにするという方法が考えれる。クライアントが正規URIをリクエストしたら200(OK)とともに適切なデータを送信する。他のURI(正規でないURI)をリクエストしたら正規URIとともに303(See Other)を送信する。こうすることで正規URIにリダイレクトさせることができる。
アドレス可能性
URIを通じてリソースを簡単に指し示せる性質のことを「アドレス可能性」と呼ぶ。アプリケーションがそのデータセットの重要な部分をリソースとして公開する場合、そのアプリケーションはアドレス可能となる。リソースはURIを通じて提供されるため、アドレス可能なアプリケーションは提供可能な情報ごとにURIを公開する。
ステートレス性
全てのHTTPリクエストが完全に分離している性質のことを「ステートレス性」と呼ぶ。サーバ側でアプリケーション状態(後述)の管理を行わない。クライアントが送信するHTTPリクエストには、サーバがそのリクエストを処理するために必要な情報が全て含まれており、以前のリクエスト情報にも依存しない。もちろんCookie等でセッション管理も行わない。
アプリケーション状態とリソース状態
状態には、アプリケーション状態とリソース状態という2つの状態が存在する。
アプリケーション状態
クライアント側で維持される状態のこと。
例えば、何らかの検索エンジンを使用する際、検索クエリや表示されている検索結果ページは人(クライアント)によって変わってくる。これは検索エンジンでなくても、オークションサイトやショッピングサイト等でも同じことが言える。このように、クライアント側で維持される状態のことをアプリケーション状態と呼ぶ(サーバ側ではアプリケーション状態を維持しない)。
リソース状態
サーバ側で維持される状態のこと。
リソースは時間の経過や条件とともに変化する可能性がある。例えば、「東京の明日の天気予報」というリソースは、前日の10:00の時点では晴れの予報だが、前日の18:00の時点では雨の予報に変わっている可能性がある。このように、サーバ側で維持される状態のことをリソース状態と呼ぶ。リソース状態は全てのクライアントに対して同じ状態が提供される。
※ちなみに、RESTは、「REpresentational State Transfer」というように、状態を転送(レスポンスとして返却)する。
表現
Webサーバはリソースを特定の言語(英語、日本語、、)、フォーマット(XML、JSON、、)で送信しなければならない。こういったリソースの形式をリソースの表現と呼ぶ。
表現の選択
サーバがリソースの表現を複数提供する場合、クライアントが要求している表現をどのようにして判断するのか?という問題が発生する。最も簡単でROA的に推奨されているのは、リソースの各表現に個別のURIを割り当てることである。例えば、リソースが英語とスペイン語で提供されている場合、
英語のURIは、
/resource.en
スペイン語のURIは、
/resource.es
といったようにそれぞれ個別のURIを割り当てる。これは、サーバがリクエストを処理するために必要な情報がすべてURIに含まれている、といった点からこの方法が推奨されている。
ただし、この方法は同じリソースなのにURIだけをみると別のリソースを指示しているように見えるという欠点もある。これの解決策として、コンテンツネゴシエーションが存在する(詳細はリンクを参照)。コンテンツネゴシエーションを利用することで、
/resource
というURIで様々な言語やフォーマットのリソースを提供することが可能となる。
リンクと接続性
リソースには別のリソースへのリンクを含めることができる。リンクを含めることで別のリソースに接続することができる。このように、リソースに含まれるリンクをもとに別のリソースに接続可能となる(遷移可能となる)性質のことを「接続性」と呼ぶ。従来のWebが使いやすいのは、リソースどうしが適切に接続されているからである。
統一インターフェース
リソースに対する最も一般的な4つの操作に対して、HTTPは以下の基本メソッドを提供している。
HTTPメソッド | 操作 |
---|---|
GET | リソースの取得 |
PUT | リソースの更新 |
POST | リソースの作成 |
DELETE | リソースの削除 |
HEAD | リソースのメタデータの取得 |
OPTIONS | リソースがサポートするメソッドを調べる |
リソースの操作をする時に、全てHTTPメソッドが定義通りに使われることを「統一インターフェース」と呼ぶ。 | |
例えば、リソースを取得する場合、/getResourceのようにURI中に操作を表す語彙(get)を使わず、/resourceのようにしてHTTPのGETメソッドを利用する。そうすることで、一貫した(統一された)インターフェースを保証している。 |
GET
クライアントは必要なリソースを取得する場合に、そのURIにGETリクエストを送信してリソースを取得する。サーバはレスポンスのエンティティボディでリソースを表現し送信する。
DELETE
クライアントは不要なリソースを削除する場合に、そのURIにDELETEリクエストを送信してリソースを削除する。DELETEの場合、レスポンスのエンティティボディはステータスメッセージを含んでいるか空である。
PUT
PUTには以下の2つの役割がある。
1.新規リソースの作成
作成したいリソースの表現をリクエストのエンティティボディに含めてPUTリクエストを送信する。例えば、以下のURIに対してPUTリクエストを送信すると、以下のURIで新しいリソースが作成される(つまりURIはクライアントが指定する)。
/weblogs/myblog/entries/1
2.既存リソースの更新
新しいリソースの表現をリクエストのエンティティボディに含めて指定したURIにPUTリクエストを送信することで、指定した既存リソースがリクエストのエンティティボディの内容で上書き(更新)される。
POST
POSTには以下の3つの役割がある。
1.親リソースに対して子リソースの作成
POSTの代表的な機能。主に掲示板、ブログ記事等の投稿に使われる。親リソースと子リソースというのは、例えばとあるブログリソース(親リソース)を
/weblogs/myblog
として提供している場合、個々のブログエントリ(子リソース)を、
/weblogs/myblog/entries/1
として提供することができる。このように、リソース間に親子関係(従属関係)が存在する場合、親リソースに対してPOSTリクエストを送信することで子リソースを作成することができる(ここでは、/weblogs/myblogに対してPOSTリクエストを送信する)。なお、作成されたリソースのURIはサーバ側で決められる。
2.既存リソースへの状態の追加
既存リソースに対して状態を追加する。例えば、ログというリソースを1つ提供するイベントログサービスがあり、そのURIが/logだとする。この既存リソースに対して新たな状態を追加する場合、/logに対してPOSTリクエストを送信することで、ログの最後にPOSTしたリソースを追加することができる。「1.親リソースに対して子リソースの作成」との違いはリソースに親子関係があるかどうかである。この場合は、/logに対してPOSTリクエストを送信すると、子リソースを新規で作成するのではなく、既存リソースにPOSTした状態を追加している。
3.オーバーロードPOST
他のメソッドでは対応できない処理の実行を行う。例えば、画面のあるWebアプリケーション等で、フォームの値を送信する、等である。こういった使い方はROAにおける統一インターフェースから外れるため、クライアントに対して挙動を明示する必要がある。極力この使い方は避けた方が良い。RESTful WebサービスではオーバーロードPOSTと命名されている。
POSTとPUTの使い分け
明確な正解はないが、設計上の指針として以下の指針が存在する。
※Webを支える技術 ── HTTP,URI,HTML,そしてRESTより引用
これには正解は存在しませんが、設計上の指針として次の事実があります。 POSTでリソースを作成する場合、クライアントはリソースのURIを指定できません。URIの決定権はサーバ側にあります。逆にPUTでリソースを作成する場合、リソースのURIはクライアントが決定します。 (中略) 一般的に、クライアントがリソースのURIを決定できるということは、クライアントを作るプログラマがサーバの内部実装(URIにどの文字を許すのか、長さの制限はどれくらいかなど)を熟知していなければなりません。 そのため、PUTのほうがどうしてもサーバとの結合が密になります。特別な理由がない限りは、リソースの作成はPOSTで行いURIもサーバ側で決定する、という設計が望ましいでしょう。
特別な理由がない限りは、リソースの作成はPOSTで行いURIもサーバ側で決定する、という設計が望ましい。
HEADとOPTIONS
HEADはリソースのメタデータを取得する。例えば、リソースのサイズ、更新日時等がそれにあたる。HEADメソッドのレスポンスにはボディが含まれない。リソースの有効性を確認したり、サイズ、更新日時を確認する場合等に利用する。OPTIONSでは、リソースがサポートしているメソッドを調べることができる。「Allow: GET, HEAD」という形でレスポンスヘッダーにAllowヘッダーが含まれるので、そこを見ることでサポートされているメソッドを確認することができる。
安全性とべき等性
HTTPの統一インターフェースを設計通りにサポートすると、2つの有益な特性をただで手に入れることができる。GETとHEADは、正しく利用すれば安全性を、GET、HEAD、PUT、DELETEはべき等性を手に入れることができる。
安全性
GET、HEADリクエストは、何らかのデータを読み取るためのリクエストであり、サーバの状態を変更するためのリクエストではない。そのため、クライアントがGET、HEADリクエストを1回実行するのも10回実行するのも同じことである。クライアントはGET、HEADリクエストを送信しても害がないことを心強く思える。ただし、これはGET、HEADリクエストが何の副作用もないということではない。ほとんどのWebサーバは受信した全てのリクエストをログファイルに記録する。これらは副作用であり、サーバの状態やリソースの状態がGETリクエストに応じて変化する。そのため、クライアントは副作用を期待してリクエストを送信してはならないし、副作用が甚大なものであってはならない。
べき等性
その操作を何度行っても結果が同じであることをいう概念を、べき等性という。GET、HEAD、PUT、DELETEがその特性を持っている。例えば、DELETEメソッドを利用して、/resource/100でリソースを削除した後に、再度、/resource/100で削除してもリソースはなくなったままである(削除されたという状態を変更できない)。PUTでも同様のことが言える。
安全性とべき等性はなぜ重要なのか
安全性とべき等性により、クライアントは信頼できないネットワーク経由で、信頼できるHTTPリクエストを送信することができる。GETリクエストを送信してレスポンスが返送されなければ、もう一度リクエストを送信すればよい。仮に、前のリクエストが処理されていたとしても、サーバに実質的な影響は及ばないので安全である(PUTやDELETE等も同様)。
統一インターフェースはなぜ重要なのか
重要なのは、全てのサービスがHTTPのインターフェースを同じ方法で使用すること、つまり統一性である。
※HTTPが定義する特定の統一インターフェースを使用することではない。
肝心なのは、GETが読み取り操作にふさわしい名前であることではなく、どのリソースに使用するかにかかわらず、GETがWeb経由での「読み取り」を意味しているということである。
このように、全てのサービスで同じインターフェースを使用することで、任意の複数のサービスを任意の複数のサイトのようにサポートできるため、分散システムを構築する際の障壁を下げることにつながる。
参考
RESTful Webサービス
RESTとリソース指向アーキテクチャについての資料
SOAの未来はRESTか
REST における PUT メソッドと POST メソッドの違い
HTTPメソッドのPOSTとPUTの使い分け