はじめに
Ruby on Rails や同種のフレームワークを使っていると、《REST思想》と《リソース指向》と《Webページ》がごちゃまぜになったWebアプリケーションをつい設計してしまいます。
三つの違いを意識し、適切なWebアプリケーションを作成するようにしましょう。でないと後悔することになります。
なお、この三つの用語は本来の意味とずれているかもしれません。
「コメント」、「編集リクエスト」大歓迎です。
解説
http://yourhost/books
のURLで本の一覧が取得できるようなWebサービスを提供するとします。
では /books
を含めた各URLはどのように振る舞うべきなのでしょうか。
(URLと言っている部分でも実際はpathを指している場合があります。ご了承ください)
REST思想
本に対してCRUD操作を行うインタフェースとして、以下のようなものを提供します。
役割 | method + URL |
---|---|
本一覧を取得 | GET /books |
一つの本を取得 | GET /books/1 |
新しい本を追加 | POST /books |
既存の本を修正 | PUT /books/1 |
既存の本を削除 | DELETE /books/1 |
各本の情報はJSONで表現されます。/books
にGETリクエストした場合はレスポンスとして本の一覧がJSONとして返されます。
本の追加や削除を行う場合は、本情報をJSON形式でPOSTリクエストのボディとして送ります。application/x-www-form-urlencoded
形式で送ることは避けるべきです。
更新に失敗した場合、失敗した理由をJSON形式で返します。
ステータスコードは、取得時・更新成功時には200 OK
を、更新失敗時には4XX番代を使うのがよいでしょう。
情報はJSON形式と書きましたが、XMLなどでも構いません。
しかし、正常系・異常系ともにアプリケーション内で統一された形式を利用してください。
リソース指向
本の情報は/books
にあるとして、拡張子を変えることで、様々なフォーマットでデータを利用できるようにします。
例えば本の一覧をJSON形式で欲しければ/books.json
にリクエストし、csv形式で欲しければ、/books.csv
にリクエストします。
/books.xls
のようにバイナリを提供してもかまいません。
更新用のAPIは用意しない方が良いでしょう。
/books.csv
に対する更新を行う場合は、はPOSTリクエストのボディとして何を送ればよいのでしょうか。CSV形式で良いのでしょうか。JSONでしょうか。
また、更新に失敗したときのエラーはどう返すべきでしょうか。もし/books.csv
にリクエストししたにも関わらず、JSON形式でエラーを返ってくることになると、クライアントは苦労するでしょう。
Webページ
サーバはHTMLを返し、ブラウザはAjaxを使わずHTML FormでデータのCRUDを行います。
Webページの場合、RESTと違い以下のようなインタフェースを提供すべきです。
役割 | URL |
---|---|
一覧画面を表示 | GET /books |
詳細画面を表示 | GET /books/1 |
追加formを表示 | GET /books/new |
追加アクション | POST /books/new |
編集formを表示 | GET /books/1/edit |
編集アクション | POST /books/1/edit |
削除確認画面を表示(任意) | GET /books/1/delete |
削除アクション | POST /books/1/delete |
REST同様にCRUD用のAPIを提供しますが、Webページの場合は更新Formを表示するためのページが必要になります。
さらに更新に失敗した際、適切にエラーを見せるには
- 更新FormページのURLとPOST先のURLを同じにする
- 成功した場合、一覧ページあるいは詳細ページにリダイレクトする
- 失敗した場合、エラーを表示し、Formを直前の入力で埋める
のがよいでしょう。
URLを同じにすることで、CSSなどのリソースのパスや、リクエストパスに依存したコードを書きやすくなります。
成功時にリダイレクトすることで、F5
対策になります。リダイレクトには303 See Other
を利用してください。
失敗時にリダイレクトせずそのまま表示することで、エラーや直前の入力をFormに表示しやすくなります。
HTML FormではリクエストメソッドとしてGETかPOSTしか利用できません。取得はGET、更新はPOSTを利用しましょう。わざわざ_method=put
のようなリクエストパラメタを利用しても、得られるものはありません。
まとめ
同じURL /books
であっても様々な設計があるということを理解してもらえたと思います。
この三つ全てをまとめて提供することは可能です。しかしControllerのコードは複雑になり、一部の機能はほとんど使われず、削除したくても一度提供したので消せないという結末に終わります。
もし複数提供する必要がある場合、URLを別にするのが良い解決案です例えば以下のように分けることができます。
(※ ただしドメイン直下は常にWebページを提供するのが良い)
目的 | URL |
---|---|
REST | http://yourhost/api/books |
リソース | http://yourhost/books |
WEBページ | http://yourhost/web/books |
要件にあわせ、素敵なアプリケーションを設計してください。