● [Rails][Rails1.1] Controller#respond_to (The accept header)
DHH たんがまったやってしまいました。HTTP の Accept フィールドに応じて render 処理を使い分けるController#respond_to というメソッドが追加されました。多くの場合、WebサービスもCGIもAjaxも出力が違うだけで処理は一緒ですので、全部を1つのアクション内で処理して、最後の出力部分だけエレガントに切り替えできればいいよね、というコンセプトのようです。
● 概要
- HTTP/1.1 リクエストヘッダ中の Accept フィールドを見る
- content-type に応じてリクエストのデータ形式を自動変換してくれる
- 従って、コントローラは同じロジック(action)で対応できる
- Accept の種類(mimeタイプ)に応じて、実行する描画処理を指定できる
- "*/*" が指定された場合は respond_to 内の最初の定義を実行する
● 書式
Controller
respond_to do |type|
type.html { render }
type.js { render :action => "#{action_name}.rjs" }'
type.xml { render :action => "#{action_name}.rxml" }'
end |
えっと何でもいいんですが、Berryz工房のメンバー情報を見ることにしましょうか。どうしても?ええ。どうしてもそれじゃないとダメなの。
MemberController
def show
@member = Member.find(params[:id])
respond_to do |type|
type.html
type.js
type.xml { render :text=>@member.to_xml }
type.rss { redirect_to :action=>:rss }
end
end |
先頭行は通常の show アクションです。上の例では、html, js, xml の3種類に対応するという宣言と、それぞれの場合の描画処理を定義しています(ブロック部分)。html, js のように処理ブロックが定義されていない場合は、デフォルトの描画処理が実行されます。先ほどの「書式」に書いてあるのがそのデフォルトのブロックです。すなわち、html の場合は render :action=>"show" が実行され、js の場合は render :action=>"show.rjs" が実行されることになります。それぞれの view を次のように準備して、実行してみます。
show.rhtml
<h1><%= @member.name %><h1>
<h2><%= @member.birthday %></h2> |
show.rjs
page.replace_html 'member_name', @member.name |
● 実行
<h1>石村舞波<h1>
<h2>1992-11-20</h2>
or '<%= link_to_remote "maiha", :url=>{:action=>"show", :id=>8} %>'
Element.update("member_name", "\u77f3\u6751\u821e\u6ce2")(補足: "\u77f3\u6751\u821e\u6ce2" はナメック語で「石村舞波」を表します)
<?xml version="1.0" encoding="UTF-8"?>
<member>
<name>石村舞波</name>
<birthday type="date">1992-11-20</birthday>
...
</member>
レスポンスヘッダ
HTTP/1.0 302 Found
location: http://localhost:3000/member/rss
... |
(未定義の mime タイプでアクセス)
HTTP/1.0 406 Not Acceptable |
● mime タイプとの関連
actionpack/lib/action_controller/mime_type.rb
ALL = Type.new "*/*", :all
HTML = Type.new "text/html", :html, %w( application/xhtml+xml )
JS = Type.new "text/javascript", :js, %w( application/javascript application/x-javascript )
XML = Type.new "application/xml", :xml, %w( text/xml application/x-xml )
RSS = Type.new "application/rss+xml", :rss
ATOM = Type.new "application/atom+xml", :atom
YAML = Type.new "application/x-yaml", :yaml, %w( text/yaml ) |
● 入力変換機能
概要2の「content-type に応じてリクエストのデータ形式を自動変換してくれる」は、
% telnet localhost 3000
POST /comments/create HTTP/1.1
Host: localhost
Accept: application/xml
Content-Type: application/xml
<member>
<name>saki</name>
<birthday type="date">1991-11-22</birthday>
</member>
というリクエストに対して、Content-Type の "application/xml" 情報から
params
member[name]=saki&member[birthday]=1991-11-22 |
のように通常のFORM入力の形式に自動変換してからアクションが呼び出される、という素晴らしい機能です。つまり、Railsが入力データ形式の差異を吸収してくれるので、アクション部分のレイヤーでは、FORMに対するロジックを書くだけで、YAMLでもXMLでも通常FORMのPOSTでも対応できるようになるのです。そして、出力に関しては前述の通り、Accept に応じて入力形式とは無関係に好きな形式を返すことができます。
| [入力] | | [前処理] | | [アクション] | | [respond_to] | | [出力] |
| XML | → | FORM化 |
→ |
FORMデータを処理 |
→ |
to_xml |
→ | XML |
| YAML | → | FORM化 | to_yaml | → | YAML |
| FORM | → | FORM化 | rhtml | → | HTML |
|
と、興奮ものの仕様なんですが、実験したところうまく動きませんでした。
log/debug
Processing MemberController#create (for 127.0.0.1 at 2006-03-17 19:13:57) [HEAD]
Session ID: f5a4b250feee5c3091449e938898eb0c
Parameters: {
"format"=>#,
"exception"=>"Could not find in (ArgumentError)",
"action"=>"create", "controller"=>"member", "raw_post_data"=>nil,
"backtrace"=>[
"./public/../config/../vendor/rails/actionpack/lib/action_controller/vendor/xml_simple.rb:977:in `find_xml_file'",
... |
dispatch.xml て何だろう?エロイ人が使い出して情報が出てくるのをゆっくり待つとしましょう。クゥ〜ン♪