大場寧子のホームページ - コントローラをRESTfulにする Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

! コントローラをRESTfulにする
!! RESTfulとは

Rails 2.0 から、RESTの特徴を備えた(=RESTFulな)アプリケーションを簡単に構築できるようになりました。簡単にいうと、HTTPリクエストのメソッド(GET, POST, PUT, DELETE)とわかりやすいURIの組み合わせで機能を表現します。

例えば、Projectというデータモデルがあるとします。従来のアプリケーション(Rails 1.0系をデフォルトの設定で作ると同様になります)では、Project の一覧、詳細表示、新規作成、更新、削除のURLは、それぞれ以下のような感じになります。

Rails1.xのデフォルトのURL例

|| 機能 || URL例
|| 一覧 || GET /projects
|| 詳細表示 || GET /projects/1
|| 新規作成画面 || GET /projects/new
|| 新規作成実行 || POST /projects/create
|| 更新画面 || GET /projects/edit/1
|| 更新実行 || POST /projects/update/1
|| 削除 || POST /projects/delete/1

Rails2.0を使ったRESTFul なURLの例

|| 機能 || URL例
|| 一覧 || GET /projects
|| 詳細表示 || GET /projects/1
|| 新規作成画面 || GET /projects/new
|| 新規作成 || POST /projects
|| 更新画面 || GET /projects/1/edit
|| 更新実行 || PUT /projects/1
|| 削除 || DELETE /projects/1

RESTful なI/Fには次のような特徴があります。

* HTTPメソッドで表現できる処理は、URLではなくメソッドで表現する。
* 同じURLでも、メソッドの種類によって異なる動作をする。
* 主語(上記でいえば、projects/1など)が先にきて、動作は後にくる。一見して意味がつかみやすい。

なお、一般的にブラウザは PUT と DELETEには対応していないため、Rails はPOST を _method パラメータ付きで呼んだリクエストも受け付けます。例えば、以下のようなフィールドを含む form を POST で送ると、PUT メソッドと同様の処理が実行されます。

<input type="hidden" name="_method" value="put" />

ブラウザでは PUT, DELETE に対応していないとしたら、なぜわざわざこのような回りくどいことをするのでしょうか? RESTful なI/Fは、現在のブラウザからアプリケーションが使われることだけを想定した機能ではありません。RESTの思想から言えば、HTTPクライアントはすべてのHTTPメソッドに対応するのが正しいのです。こうした考えのもと、本来対応すべきHTTPメソッドに対応していない現在のブラウザのために回避措置として_methodフィールドが用意されています。

なお、明示的にhiddenフィールドを用意するほか、form_tag や form_for で以下のように指定しても同様の結果が得られます。

form_tag
<%= form_tag project_path(@message), :method => :put %>

form_for
<% form_for :project, @project, :url => project_path(@project), :html => {:method => :put} do |f| %>

<% form_for @project do |f| %>

!! RESTFul な I/Fにする

コントローラのI/FをRESTful にするには、roots.rb で map.resource または map.resources を使います。((-ここでいうリソースは、扱うモデルインスタンスのことであると考えると理解しやすいと思います。-))次の例では、ProjectsController の I/F を map.resources を使って RESTful にしています。

ActionController::Routing::Routes.draw do |map|
   map.resources :projects

   ......
end

このように指定すると、次のようにI/Fとアクションが結びつけられます。((-対応するアクションをmap.resourcesが作ってくれるわけではありません。Scaffold や 手動で作っておく必要があります。-))

map.resources で自動的にマッピングされるI/Fとアクション

|| I/F ||アクション||パラメータ
|| GET /projects || index
|| GET /projects/new || new
|| POST /projects || create
|| GET /projects/1 || show || :id -> 1
|| GET /projects/1/edit || edit || :id -> 1
|| PUT /projects/1      || update || :id -> 1
|| DELETE /projects/1  || destroy || :id -> 1

もちろん、コントローラによっては、上記とは異なるアクション構成となることでしょう。その場合は、map.resources にオプションを指定することでカスタマイズができます。詳しくは、「((<コントローラのRESTfulインターフェースをカスタマイズする>))」を参照してください。

なお、map.resource という単数形のほうのメソッドを使うと、扱う対象がつねに1つ(プロジェクト一覧のような機能が不要で、システム内で1つのインスタンスしかない:singleton)であるとみなしてI/Fを作成してくれます。一覧機能が不要なためindexアクションはなく、対象が1つなので :id を使わない形で new, create, show, edit, update, destroy とURLが紐づくとして設定されます。

!! 名前つきルートとURLヘルパーメソッド

map.resource や map.resources を1回呼ぶことは、名前つきルートを複数同時に作成することになります。あわせて、名前つきルートに対応するURLヘルパーメソッドも複数作成されます。

map.resources :projects

上記に対して作成される名前付きルートとURLヘルパーメソッドは次のようになります。show、update、destroyはURLとしては同じなので、URLを使う際にはHTTPメソッドにも注意しましょう。

map.resources :projects で生成される名前付きルートとヘルパーメソッド

|| アクション || 名前つきルートの名前 || ヘルパーメソッド
|| index || projects || projects_url, hash_for_projects_url, projects_path, hash_for_projects_path
|| show, update, destroy  || project || project_url, hash_for_project_url, project_path, hash_for_project_path
|| new   || new_project || new_project_url, hash_for_new_project_url, new_project_path, has_for_new_project_path
|| edit  || edit_project || edit_project_url, hash_for_edt_project_url, edit_project_path, hash_for_edit_project_path