2006-03-17 優しいRailsの育て方 [長年日記]

[Rails][Rails1.1] Controller#respond_to (The accept header)

DHH たんがまったやってしまいました。HTTP の Accept フィールドに応じて render 処理を使い分けるController#respond_to というメソッドが追加されました。多くの場合、WebサービスもCGIもAjaxも出力が違うだけで処理は一緒ですので、全部を1つのアクション内で処理して、最後の出力部分だけエレガントに切り替えできればいいよね、というコンセプトのようです。

概要

  1. HTTP/1.1 リクエストヘッダ中の Accept フィールドを見る
  2. content-type に応じてリクエストのデータ形式を自動変換してくれる
  3. 従って、コントローラは同じロジック(action)で対応できる
  4. Accept の種類(mimeタイプ)に応じて、実行する描画処理を指定できる
  5. "*/*" が指定された場合は 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

実行

% wget http://localhost:3000/member/show/8

<h1>石村舞波<h1>
<h2>1992-11-20</h2>

% wget --header='Accept: application/javascript' http://localhost:3000/member/show/8

or '<%= link_to_remote "maiha", :url=>{:action=>"show", :id=>8} %>'

Element.update("member_name", "\u77f3\u6751\u821e\u6ce2")

(補足: "\u77f3\u6751\u821e\u6ce2" はナメック語で「石村舞波」を表します)

% wget --header='Accept: application/xml' http://localhost:3000/member/show/8

<?xml version="1.0" encoding="UTF-8"?>
<member>
  <name>石村舞波</name>
  <birthday type="date">1992-11-20</birthday>
  ...
</member>

% wget --header='Accept: application/rss+xml' http://localhost:3000/member/show/8

レスポンスヘッダ

HTTP/1.0 302 Found
  location: http://localhost:3000/member/rss
  ...

% wget --header='Accept: text/yaml' http://localhost:3000/member/show/8

(未定義の 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][出力]
XMLFORM化 FORMデータを処理 to_xml XML
YAMLFORM化to_yamlYAML
FORMFORM化rhtmlHTML

と、興奮ものの仕様なんですが、実験したところうまく動きませんでした。

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 て何だろう?エロイ人が使い出して情報が出てくるのをゆっくり待つとしましょう。クゥ〜ン♪


サイト内検索 (by Google)

| JRuby | Rails | Berryz | ℃-ute | エッグ | jQuery |

過去

2006年
3月
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

未来

コンタクト