Last-modified: 2010-09-23 (木) 13:59:42 (1d)

Action Controller Overview

原文

http://guides.rubyonrails.org/action_controller_overview.html

Action Controller 概要(Action Controller Overview)

このガイドではどのようにコントローラは動いているのか、どのようにアプリケーション内でリクエストサイクルに合わせているのかを学ぶことができます。
このガイドを読んだ後、以下のことができるようになります:

  • コントローラを通じたリクエストの流れに沿う
  • なぜ、どのようにこのセッションやクッキー内にデータを保存するのかを理解する
  • リクエストが処理されている間、コードを実行するためのフィルターの働き
  • Action Controller の組み込み HTTP 認証の使用
  • ユーザーのブラウザへ直接データをストリームする
  • アプリケーションのログに現れないようにするための敏感なパラメータのフィルタリング
  • リクエストの処理中に起きるかもしれない例外の処理

1 コントローラは何をしますか?(What Does a Controller Do?)

Action Controller は MVC の中の C です。
ルーティングがリクエストのために使うコントローラを決定した後、コントローラは
リクエストが意味を成すこと・ふさわしいアウトプットの生成をすることに対して責任があります。
幸運なことに、 Action Controller は下地のほとんどをしてくれて、これをできる限り簡単に作るために、スマートな規約を使用します。

ほとんどの従来の RESTful なアプリケーションにとって、コントローラはリクエストを受け取り(これは開発者には見えない)
モデルからデータを取得や保存し、 HTML 出力を作るためのビューを使用します。
もしコントローラが少し違ったことをする必要があっても、問題ありません。
これはコントローラにとって、最も一般的な動き方なだけです。

コントローラは、このようにモデルとビューの間の中間的なものとして考えられています。
モデルのデータをビューが利用できるようにし、それによりユーザーにそのデータを表示でき、ユーザーからのデータをモデルに保存・更新します。

ルーティングの過程のさらに詳しいことは Rails Routing from the Outside In を見てください。

2 メソッドとアクション(Methods and Actions)

コントローラは ApplicationController を継承した Ruby クラスであり、他のクラスと同様にメソッドを持っています。
アプリケーションがリクエストを受け取ったとき、ルーティングはどのコントローラ、アクションで動かすかを決定し、
Rails はコントローラのインスタンスを生成し、そのアクションと同じ名前のメソッドを動かすでしょう。

class ClientsController < ApplicationController
  def new
  end
end

例として、もしユーザーが、新しい顧客を追加しようとして、あなたのアプリケーション上の /clients/new に行く場合、
Rails は ClientsController のインスタンスを作り、 new メソッドを実行するでしょう。
アクションが違うことを命じない限り、 Rails はデフォルトで、 new.html.erb ビューをレンダリングするので、
上の例の空っぽのメソッドは正確に動くことに注意してください。
new メソッドは新しい Client を作ることによって、ビューが @client インスタンス変数を使用可能にできます。

def new
  @client = Client.new
end

Layouts & rendering guide はこれをさらに詳しく説明しています。

ApplicationController は ActionController::Base を継承しており、これはたくさんの役に立つメソッドを定義しています。
このガイドはこれらのいくつかをカバーしていますが、何があるか気になるなら、APIドキュメントまたはソース自体で全てを見ることができます。

パブリックなメソッドだけを、アクションとして呼び出すことができます。
補助メソッドやフィルターのような、アクションであることを意図していないメソッドの可視性を下げるためのベストプラクティスです。

3 パラメーター(Parameters)

コントローラのアクション内で、ユーザーによって送られたデータや他のパラメータにアクセスしたいでしょう。
ウェブアプリケーション内で可能なパラメータには二種類あります。
一つは URL の一部として送られるパラメータで、これはクエリ文字列パラメータと呼ばれます。
クエリ文字列は URL 内の "?" の後ろの全てです。
二つ目のパラメータのタイプは普通、 POST データとされています。
この情報は通常、ユーザーによって書き込まれた HTML フォームからきます。
HTTP POST リクエストの一部としてだけ送られるので、これは POST データと呼ばれます。
Rails はクエリ文字列パラメータと POST パラメータの区別はつけず、両方ともコントローラ内の params ハッシュで利用できます。

class ClientsController < ActionController::Base
  # このアクションは HTTP GET リクエストにより実行されるので、クエリ文字列を使用します。
  # しかし、パラメータがどの方法でアクセスされようと、いかなる違いもありません。
  # このアクションの URL は、認証された顧客をリスティングするために
  # /clients?status=activated のようになるでしょう。
  def index
    if params[:status] == "activated"
      @clients = Client.activated
    else
     @clients = Client.unactivated
    end
  end

  # このアクションは POST パラメータを使用します。
  # ユーザーが HTML フォームから送信したものが大半です。
  # この RESTful な URL は "/clients" になるでしょう。
  # そして、データはリクエスト本文の一部となって送られるでしょう。 
  def create
    @client = Client.new(params[:client])
    if @client.save
      redirect_to @client
    else
      # この行は、 "create" ビューをレンダリングするという、デフォルトのレンダリングの
      # 振る舞いをオーバーライドしています。
      render :action => "new" 
    end
  end
end

3.1 ハッシュ、配列パラメータ(Hash and Array Parameters)

この params ハッシュは、一次元のキーとバリューに制限されていません。
配列や(ネストした)ハッシュを含むことができます。 値の配列を送るために、キーの名前に "[]" を付け加えます:

GET /clients?ids[]=1&ids[]=2&ids[]=3

この例の実際の URL は "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3" としてエンコードされ、 "[" , "]" は URL に認められません。
ブラウザが代わりにやってくれたり、 Rails がそれを受け取るときにデコードしてくれるにで、ほとんどの場合、そのことについて心配することはありません。
しかしもし常に手動でサーバにリクエストを送るのなら、これを覚えておかなければならないでしょう。

いま、 params[:ids] の値は ["1", "2", "3"] です。
パラメータ値は常に文字列であることに注意して下さい。
Rails は型を推測したりキャストしたりしません。

ハッシュを送信するには、 [] の中にキー名を含ませます。

<form action="/clients" method="post">
  <input type="text" name="client[name]" value="Acme" />
  <input type="text" name="client[phone]" value="12345" />
  <input type="text" name="client[address][postcode]" value="12345" />
  <input type="text" name="client[address][city]" value="Carrot City" />
</form> 

このフォームが送られたとき、 params[:client] の値は {"name" => “Acme", “phone" => “12345", “address" => {"postcode" => “12345", “city" => “Carrot City"}} になります。
params[:client][:address] 内のネストしたハッシュに注意してください。

params ハッシュは実際は Active Support の HashWithIndifferentAccess のインスタンスです。
これはシンボルt文字列を交互にキーとして使うハッシュのように動作します。

3.2 ルーティングパラメータ(Routing Parameters)

params ハッシュは常に :controller と :action キーを格納していますが、
それらの値にアクセスする代わりに controller_name や action_name メソッドを使うべきでしょう。
:id のようなルーティングにより定義された、他のどのようなパラメータもまた利用できるでしょう。
例として、顧客がアクティブか非アクティブかを示すことできるリストのリスティングを検討してみましょう。
私たちは "プリティな" URL 内の :status パラメータを見つけることで、ルーティングを加えることができます。

map.connect "/clients/:status",
  :controller => "clients",
  :action => "index",
  :foo => "bar"

この場合、ユーザーが /clients/active の URL を開いたとき、 params[:status] は "active" にセットされます。
このルーティングが使われたとき、クエリ文字列で渡されるのと同様に、 params[:foo] もまた "bar" にセットされるでしょう。
同じように params[:action] は "index" を含むでしょう。

3.3 default_url_options(default_url_options)

URL を生成するときに使われるであろうグローバルなデフォルトパラメータを、 default_url_options でセットすることができます。
こうするために、コントローラ内でメソッドを定義して下さい。

class ApplicationController < ActionController::Base
  # オプションのパラメータは 'url_for' に渡されるハッシュです。
  def default_url_options(options)
    {:locale => I18n.locale}
  end
end

これらのオプションは URL を生成するときの出発点として使われるでしょう、 url_for によってオーバーライドされることが可能です。
コントローラ内でこのメソッドは定義されるので、
ApplicationController 上でそれを定義することができ、そのためそれはすべての URL の生成に使われます。
もしくは、メソッドはたった一つのコントローラ上で定義することができ、そこで生成されたすべての URL のために使われます。

4 セッション(Session)

アプリケーションはそれぞれのユーザーのためにセッションを持っていて、少しのデータを保存することができるので、リクエスト間で永続化するでしょう。
セッションはコントローラとビュー内でだけ利用することができ、多くの様々な記憶装置のなかの一つを使うことができます:

  • CookieStore ― 顧客にすべて保存します。
  • DRbStore ― DRb サーバにデータを保存します。
  • MemCacheStore ― memcache にデータを保存ます。
  • asbtiveRecordStore ― Active Record を使用しているデータベースにデータを保存します。

全てのセッションストアは、それぞれのセッションのためにユニークな ID を保存するのに cookie を使います。
( cookie を使わなければいけません。安全性が低いので Rails は URL 内のセッション ID を渡すことができません。)

ほとんどのストアでは、この ID はサーバ上でセッションデータを調べるために使われます。(例:データベーステーブル内)
例外が一つあり、それはデフォルトであり推奨されているセッションストアである CookieStore です。
その場合、クッキー自体にすべてのセッションデータを保存しています。(その ID は必要であれば利用することができます)
これはとても軽量だという利点を持っており、セッションを使用するために新しいアプリケーション内で何も設定を必要としません。
このクッキーデータは改竄防止のために、暗号署名されていますが、
暗号化されていないので、どの人も内容を読むことはできますが、編集することはできません。(もし編集されていると Rails は受け付けません。)

クッキーストアはおよそ 4kb のデータを保存できます。これは他よりとても少ないが、普通これで十分です。
アプリケーションが使用しているどのセッションストアでも、セッションで大量のデータを格納することは、お勧めしません。
サーバがリクエスト間で再び組み立てることができないかもしれないので、特にセッション内で複雑なオブジェクト
(基本的な Ruby object 以外のなにか。最も一般的な例はモデルインスタンス)の保存を避けなければなりません。
それは結果エラーになるでしょう。

セッションの保存について詳しいことは Security Guide を読んでください。

もし違うセッションの保存のメカニズムについて必要なら、config/initializers/session_store.rb 内で変更することができます:

# クッキーベースのデフォルトの代わりにセッションにデータベースを使うなら、
# 機密性の高い情報を格納するために使用すべきではありません。
# ( "rake db:sessions:create" でセッションテーブルを作成してください。)
# YourApp::Application.config.session_store :active_record_store

Rails はセッションデータを署名するとき、セッションキー(クッキーの名前)を設定します。
これらは config/initializers/session_store.rb でも変更することができます:

# このファイルを変更した時、必ずサーバーを再起動してください。

YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session'

また、 :domain キーを渡し、クッキーのためにドメイン名を指定することも出来ます。

# このファイルを変更した時、必ずサーバーを再起動してください。

YourApp::Application.config.session_store :cookie_store, :key => '_your_app_session', :domain => ".example.com"

Rails は(クッキーストアのために) 、セッションデータを署名するために使う秘密鍵を設定します。
これは config/initializers/secret_token.rb 内で変更出来ます。

# このファイルを変更した時、必ずサーバーを再起動してください。

# 署名されたクッキーの整合性を検証するための秘密鍵。
# この鍵を変更すると、全ての古いセッションは無効となるでしょう!
# :secretは少なくとも30文字でかつ全てランダムで、基本的な単語ではない言葉にしないと、
# 辞書攻撃に晒されるでしょう。

YourApp::Application.config.secret_token = '49d3f3de9ed86c74b94ad6bd0...'
 

CookieStore を使っているときに秘密鍵を変えることは、すべての存在するセッションを無効にするでしょう。

4.1 セッションへのアクセス(Accessing the Session)

コントローラ内では、session インスタンスメソッドを通してセッションにアクセスできます。

セッションは Lazy Loading されます。
アクションのコードのなかでセッションにアクセスしないなら、セッションはロードされないでしょう。
したがって、無効なセッションが必要になることは無く、それらにアクセスしていないものだけが働くでしょう。

セッション値は、ハッシュのようにキー/値のペアーを使って保存されます。

class ApplicationController < ActionController::Base

private

  # セッションの :current_user_id キーに保存された ID によってユーザーを検索して下さい。
  # Rails アプリでユーザー認証を制御する一般的な方法です。
  # ログインでセッション値を設定し、ログアウトで削除します。

  def current_user
    @_current_user ||= session[:current_user_id] &&
      User.find(session[:current_user_id])
  end
end

セッションに何かを保存するのは、ハッシュのようにキーを指定するだけです:

class LoginsController < ApplicationController
  # ログインの "作成" 、別名 "ユーザーのログイン"
  def create
    if user = User.authenticate(params[:username], params[:password]) 
      # ユーザー ID をセッションに保存しましょう、
      # それは後続のリクエストで使用できます。
      session[:current_user_id] = user.id
      redirect_to root_url
    end
  end
end

セッションから何かを除くには、キーに null を指定します:

class LoginsController < ApplicationController
  # ログインの "削除" 、別名 "ユーザーのログアウト"
  def destroy
    # セッションからユーザー"ID"を削除しましょう。
    session[:current_user_id] = nil
    redirect_to root_url
  end
end

全てのセッションをリセットするには、 reset_session を使います。

4.2 フラッシュ(The Flash)

フラッシュは、それぞれのリクエストとともにクリアされるセッションの特別な部分です。
これが意味するのは、フラッシュに保存された値は次のリクエスト内でだけ利用できるということです。
これは、エラーメッセージなどの保存に役に立ちます。
ハッシュのように、セッションとして同じ方法でアクセスされます。
例として、ログアウトの実行を使ってみましょう。
このコントローラは次のリクエスト上で、ユーザーに表示されであろうメッセージを送ることができます。

class LoginsController < ApplicationController
  def destroy
    session[:current_user_id] = nil
    flash[:notice] = "You have successfully logged out"
    redirect_to root_url
  end
end

この destroy アクションはアプリケーションの root_url にリダイレクトし、そこにメッセージが表示されるでしょう。
前のアクションがフラッシュ内に保存するものがなんであれ、何かを決めるのは完全に次のアクションだということに注意しましょう。
アプリケーションのレイアウト内でフラッシュから起こるエラーや通知が表示されることは、よくあります。

<html>
  <!-- <head/> -->
  <body>
    <% if flash[:notice] -%>
      <p class="notice"><%= flash[:notice] %></p>
    <% end -%>
    <% if flash[:error] -%>
      <p class="error"><%= flash[:error] %></p>
    <% end -%>
    <!-- more content -->
  </body>
</html> 

この方法では、アクションがエラーまたは通知メッセージを設定した場合、レイアウトが自動的に表示されるでしょう。

フラッシュの値を他のリクエストに持ち越したいなら、 keep メソッドを使って下さい。

class MainController < ApplicationController
  # 例えばこのアクションは root_url に対応していますが、
  # 全てのリクエストを UsersController#index にリダイレクトしたいとしましょう。
  # アクションがフラッシュを設定し、そこにリダイレクトしたなら、値は通常、他のリダイレクトが起こった時失われますが、
  # keep を使えば、他のリクエストのため保持し続けることができます。
  def index

    # 全てのフラッシュ値を保持し続けさせましょう。
    flash.keep

    # 色んな種類の値を保持し続けるためにキーを使うこともできます。
    # flash.keep(:notice)

    redirect_to users_url
  end
end

4.2.1 flash.now

デフォルトでは、フラッシュに値を追加することは、次のリクエストでその値を利用可能にしますが、
時々、同じリクエスト内のこれらの値にアクセスしたい時もあるかもしれません。
例えば、もし create アクションがリソースを保存するのに失敗して、new テンプレートを直接レンダリングするなら、
新しいリクエストではありませんが、フラッシュを使ってメッセージを表示させたいかもしれないでしょう。
これをするためには、普通の flash と同じ方法で flash.now を使うことができます:

class ClientsController < ApplicationController
  def create
    @client = Client.new(params[:client])
    if @client.save
      # ...
    else
      flash.now[:error] = "Could not save client"
      render :action => "new" 
    end
  end
end

5 クッキー(Cookies)

アプリケーションはクライアントに少量のデータを保存できます。
これはクッキーとよばれ、リクエストやセッションさえも越えて残っているでしょう。
Rails は cookies メソッドを介して、クッキーへの簡単なアクセスを提供しています。
これは session とよく似ていて、ハッシュのように動きます:

class CommentsController < ApplicationController
  def new
    # クッキーに保存されていた場合、コメント者の名前をオートフィルします。
    @comment = Comment.new(:name => cookies[:commenter_name]) 
  end

  def create
    @comment = Comment.new(params[:comment])
    if @comment.save
      flash[:notice] = "Thanks for your comment!"
      if params[:remember_name]
        # コメント者の名前を思い出します。
        cookies[:commenter_name] = @comment.name
      else
       # コメント者の名前のクッキーがあれば削除します。
       cookies.delete(:commenter_name)
      end
      redirect_to @comment.article
    else
      render :action => "new"
    end
  end
end

セッション値のキーを nil に設定する時は注意して下さい、クッキー値を削除するためには cookies.delete(:key) を使うべきです。

6 xml と json データのレンダリング(Rendering xml and json data)

ActionController は xml や json データのレンダリングを非常に簡単にします。
scaffold を使ってコントローラーを生成するなら、このようになるでしょう:

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html # index.html.erb
      format.xml { render :xml => @users}
    end
  end
end

上記の場合のコードは、 render :xml => @users で、 render :xml => @users.to_xml ではないことに注意して下さい。
その入力が文字列でなければ、 Rails は自動的に to_xml を実行するからです。

7 フィルタ(Filters)

フィルタは、コントローラのアクションの前、後、 "前後" に実行されるメソッドです。

フィルタは、継承されるので、 ApplicationController 上でフィルタを設定した場合、
アプリケーション内のすべてのコントローラ上で実行されるでしょう。

before フィルタは、リクエストのサイクルを停止する場合があります。
一般的な before フィルタの一つは、アクションを実行するために、ユーザーがログインすることを求めるものです。
フィルタのメソッドをこの方法で定義できます:

class ApplicationController < ActionController::Base
  before_filter :require_login

  private

  def require_login
    unless logged_in?
      flash[:error] = "You must be logged in to access this section"
      redirect_to new_login_url # リクエストのサイクルを停止します
    end
  end


  # logged_in? メソッドは、ユーザーがログインしていれば true 、そうでなければ false を単に返すだけです。
  # 以前作成した current_user メソッドを !! 演算子を使って、ブーリアン化するものです。
  # これは Ruby で一般的ではなく、本当に true か false に変換したい場合以外は推奨されないことに注意して下さい。
  def logged_in?
    !!current_user
  end
end

ユーザーがログインしていない場合、そのメソッドは単にフラッシュ内にエラーメッセージを格納し、ログインフォームにリダイレクトします。
before フィルタが、レンダリングまたはリダイレクトする場合、アクションは実行されないでしょう。
その後に追加でフィルタを実行するようにスケージュールしている場合、キャンセルされます。

この例ではフィルタは ApplicationController に追加されているので、アプリケーション内のすべてのコントローラはそれを継承します。
これはアプリケーション内の全てにおいて、それを使用するためにユーザーにログインを要求するでしょう。
明白な理由(ユーザーが初めてきた場所でログインすることができないだろう!)により、すべてのコントローラやアクションで、要求すべきではないでしょう。
skip_before_filter で特定のアクションの前に、このフィルタの実行を防げられるでしょう。

class LoginsController < Application
  skip_before_filter :require_login, :only => [:new, :create]
end

今では、 LoginController の new と create アクションは、ユーザーにログインを要求することなく、動作するでしょう。
:only オプションは、これらのアクションでだけこのフィルタをスキップするために使われます。
また、 :except オプションは、その他の場合だけ動作するようにします。
これらのオプションは、フィルタを追加する時にも使用することができます。
最初に選択したアクションでだけ実行するフィルタを追加することができます。

7.1 after フィルタと around フィルタ(After Filters and Around Filters)

before フィルタに加えて、アクションの後や、前後にフィルタを実行することができます。
after フィルタは、 before フィルタと同様ですが、アクションが既に実行されているので、
クライアントに送信されるのレスポンスデータへのアクセスがあります。
明白なことですが、 after フィルタはアクションの実行を止めることはできません。

around フィルタはアクションを実行するための責任がありますが、
アクションの実行を、 around フィルタの方法で止めることを選択できます。

# Railsのフィルタに関するAPI文書から例を取りました:
# http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html
class ApplicationController < Application
  around_filter :catch_exceptions

  private

  def catch_exceptions
    yield
  rescue => exception
    logger.debug "Caught exception! #{exception}"
    raise 
  end
end

7.2 フィルタを使う他の方法(Other Ways to Use Filters)

フィルタを使う最も一般的な方法は、 private メソッドを作り、それらを追加する為に *_filter を使うようにすることで、
同じことを行う方法は他にも二つあります。

1番目は、 *_filter メソッドと共に直接ブロックを使う方法です。
ブロックは引数としてコントローラーを受け取り、上記の require_login フィルタをブロックを利用するために上書きします:

class ApplicationController < ActionController::Base
  before_filter do |controller|
    redirect_to new_login_url unless controller.send(:logged_in?) 
  end
end

この場合のフィルタが send を使用していることに注意してください。 なぜなら、 logged_in? メソッドは private で、フィルタがこのコントローラーのスコープでは動作しないからです。
これは、この特定のフィルタを実装するのに推奨される方法ではありませんが、もっと単純なケースでは便利でしょう。

2番目の方法は、フィルタリングを制御する為にクラスを使用すること(実際には、適切なメソッドに対応するオブジェクトが使用されるでしょう)です。
複雑で、他の二つの方法では読みやすく再利用が可能なように実装できない場合に、便利です。
例として、ログインフィルタをクラスを使用して再実装しましょう。

class ApplicationController < ActionController::Base
  before_filter LoginFilter
end

class LoginFilter
  def self.filter(controller)
    unless controller.send(:logged_in?)
      controller.flash[:error] = "You must be logged in"
      controller.redirect_to controller.new_login_url
    end 
  end
end

繰り返しますが、これはこのフィルタのための理想的な例ではありません。
なぜなら、コントローラのスコープ内で実行されないからですが、コントローラが引数として渡されます。
filter クラスは、 before フィルタか after フィルタかに依存する、アクションの前か後に実行するクラスメソッド filter を持っています。
around フィルタとして使われたクラスもまた、同じ filter メソッドを持っていますが、同様に動作するでしょう。
そのメソッドはアクションを実行するため yield しなければなりません。
代わりに、アクションの前、後に実行する、 before メソッド、 after メソッドの両方を持っています。

Rails API 文書には、フィルタに関する他の情報も載っています。

8 ベリファイケーション(検証)(Verification)

ベリファイケーションは、コントローラーやアクションを実行するために特定の条件が満たされていることを確認します。
params 、 session 、 flash ハッシュ、使われた特定の HTTP メソッド、 XMLHttpRequest ( Ajax ) を使って作られたリクエスト内に
存在する特定のキー(または配列形式で複数のキー)を指定できます。
デフォルトのアクションは、これらの条件が満たされていない場合、 400 Bad Request の応答をレンダリングすることですが、
これは、リダイレクト URL やレンダリングする何かを指定することでカスタマイズすることができ、
また、フラッシュメッセージと HTTP ヘッダをレスポンスに追加できます。
これは、"本質的には before_filter の特別な種類" として、APIドキュメントに記載されています。

ログインするために、ユーザー名とパスワードを入力し、それをベリファイケーションを使用して確認する例です:

class LoginsController < ApplicationController
  verify :params => [:username, :password],
         :render => {:action => "new"},
         :add_flash => {
           :error => "Username and password required to log in"
         }

  def create
    @user = User.authenticate(params[:username], params[:password])
    if @user
      flash[:notice] = "You're logged in"
      redirect_to root_url
    else
      render :action => "new" 
    end
  end
end

今では、 create アクションは "username" と "password" パラメーターが存在しなければ実行されません、
そしてパラメーターが存在しなけば、エラーメッセージがフラッシュに追加され、 new アクションがレンダリングされるでしょう。
しかし、上記ベリファイケーションを失敗したすることより重要なことがあります:
それは、望んでいなかろうと、 LoginsController 内のすべてのアクションに使用されるでしょう。
フィルタのように :only と :except を用いて、アクションに制限をかけることができます。

class LoginsController < ApplicationController
  verify :params => [:username, :password],
         :render => {:action => "new"},
         :add_flash => {
           :error => "Username and password required to log in"
         },
   :only => :create # "create" アクションでだけ動作します
end

9 リクエストの偽造防止(Request Forgery Protection)

クロスサイトリクエスト偽造は、あるサイトがユーザーを騙し、他サイト上のリクエストを作り、
ユーザーの知識や権限なしに、他サイト上のデータを、追加、修正、削除を可能にするという種類の攻撃です。

これを防ぐ最初のステップは、全ての "破壊的な" アクション( create 、 update 、 destroy )は GET リクエストではアクセスさせないことです。
RESTful な規則に従っているなら、あなたはすでにこれを行っています。
しかしながら、悪意のあるサイトはあなたのサイトに GET でないリクエストですらかなり簡単に送ることができます、
そこでリクエストの偽造防止の出番です。
その名の通り、偽造リクエストから保護します。

これを行う方法は、サーバーしか知らない、推測できないトークンを全てのリクエストに追加することです。
この場合、リクエストが適切なトークン無しで来た場合、アクセスを拒否するでしょう。

このようなフォームを生成する場合、

<% form_for @user do |f| -%>
  <%= f.text_field :username %>
  <%= f.text_field :password -%>
<% end -%> 

隠しフィールドとして、どのようにトークンが追加されているか見れるでしょう:

<form action="/users/1" method="post">
<input type="hidden"
       value="67250ab105eb5ad10851c00a5621854a23af5489"
       name="authenticity_token"/>
<!-- fields -->
</form> 

Rails は form helper を使用して生成したすべてのフォームに、このトークンを追加するので、
ほとんどの場合、そのことについて心配する必要はありません。
手動でフォームを作成している場合、または別の理由でトークンを追加する必要がある場合、
form_authenticity_token メソッドを通じて、利用可能です。

form_authenticity_token は、有効な認証トークンを生成します。
カスタムした Ajax を呼び出すように、自動的にトークンが付加されない時、非常に便利です。

Security Guideは、このことおよび、
Web アプリケーションを開発する際に注意する必要があるその他のセキュリティ関連の問題について多くの詳細が書いてあります。

10 Request 、Response オブジェクト(The Request and Response Objects)

全てのコントローラー内に、現在実行中のリクエストサイクルに関連付けられた
request と response オブジェクトを指している二つのアクセサメソッドがあります。
request メソッドは、 AbstractRequest のインスタンスを格納しており、
response メソッドは、クライアントに送り返す内容を表す response オブジェクトを返します。

10.1 request オブジェクト(The request Object)

クライアントから来るリクエストについての便利な情報を、 request オブジェクトは沢山格納しています。
利用可能なメソッドの完全なリストを取得するには、 API ドキュメントを参照してください。
このオブジェクト上のアクセス出来るプロパティは:

request のプロパティ目的
hostこのリクエストに使われた hostname
domain(n=2)ホスト名の最初の n 個のセグメント、右から始まる。(Top Level Domain)
formatクライアントによってリクエストされた、 content type
methodリクエストに使われた HTTP メソッド
get?, post?, put?, delete?, head?HTTP メソッドが GET/POST/PUT/DELETE/HEAD の場合、 true を返します
headersリクエストに関連付けられたヘッダを格納するハッシュを返します
portリクエストに使われたポート番号(整数)
protocol使われたプロトコルに "://" を足した文字列を返します 例:"http://"
query_stringURL のクエリ文字列部分 すなわち "?" の後の全て
remote_ipクライアントの IP アドレス
urlリクエストに使われた URL 全体

10.1.1 path_parameters 、 query_parameters 、 request_parameters

Rails はクエリ文字列の一部か post の body の一部として送られたに関わらず、
params ハッシュ内の、リクエストと共に送られた全てのパラメータを収集します。
どこから来たかに依存するこれらのパラメータへの、アクセスを与える出来る三つのアクセサを、 request オブジェクトは持っています。
query_parameters ハッシュはクエリ文字列の一部として送信されたパラメータを格納し、
request_parameters ハッシュは post body の一部として送られたパラメータを格納します。
path_parameters ハッシュはルーティングによって、特定のコントローラーとアクションに導くパスの部分として理解されたパラメータを格納します。

10.2 response オブジェクト(The response Object)

response オブジェクトは、通常直接使用されませんが、
アクションの実行とユーザーに送信されるデータのレンダリングの間に構築され、
そして時々 - after フィルタ内のような場所で - response に直接アクセスする場合に役立ちます。
これらのアクセサメソッドにもセッターを持っているものもあって、それらの値を変更することができます。

response のプロパティ目的
bodyこれは顧客に送信された文字列のデータです。 ほとんどの場合 HTML です。
statusレスポンスの HTTP status code です。 成功したリクエストには 200 ファイルが見つからない場合は 404 のように返します。
locationクライアントがリダイレクトされる URL (あれば)
content_typeレスポンスの content type
charsetレスポンスに使われている character set 。デフォルトは "utf-8"
headersレスポンスに使われているヘッダ

10.2.1 カスタムヘッダの設定

response のためのカスタムヘッダーを設定したい場合、 response.headers はちょうどいいです。
ヘッダ属性は、ヘッダ名にそれらの値をマップしているハッシュであり、 Rails は自動的にそれらのいくつかを設定するでしょう。
ヘッダーを追加する場合、または変更する場合、 response.headers にこのように割り当てるだけです。

response.headers["Content-Type"] = "application/pdf" 

11 HTTP認証(HTTP Authentications)

Rails は2つの組み込み HTTP 認証メカニズムを持っています:

  • Basic 認証
  • Digest 認証

11.1 HTTP Basic 認証(HTTP Basic Authentication)

HTTP Basic 認証は、ブラウザとその他の HTTP クライアントの大半でサポートされている認証スキームです。
例として、ブラウザの HTTP basic ダイアログウインドに username と password を入力して使う、管理者セクションについて検討しましょう。
組み込みの認証を使うのはとても簡単で、 authenticate_or_request_with_http_basic メソッドを呼び出すだけです。

class AdminController < ApplicationController
  USERNAME, PASSWORD = "humbaba", "5baa61e4"

  before_filter :authenticate

  private

  def authenticate
    authenticate_or_request_with_http_basic do |username, password|
      username == USERNAME &&
      Digest::SHA1.hexdigest(password) == PASSWORD
    end
  end
end

この場所で、 AdminController を継承した namespace 化されたコントローラを作れます。
before フィルタは、 HTTP Basic 認証で保護するために、これらのコントローラ内の全てのアクションで実行されるでしょう。

11.2 HTTP Digest 認証(HTTP Digest Authentication)

HTTP Digest 認証は、ネットワーク越しに暗号化されていない password を送信しろと要求しない点で、 Basic 認証より優れています。
(ただし、 HTTP Basic 認証は、 HTTPS 経由でなら安全です)
Rails による Digest 認証を使うことはとても簡単で、 authenticate_or_request_with_http_digest メソッドを呼び出すだけです。

class AdminController < ApplicationController
  USERS = { "lifo" => "world" }

  before_filter :authenticate

  private
  def authenticate
    authenticate_or_request_with_http_digest do |username|
      USERS[username]
    end
  end
end

上記の例に見られるよう、 authenticate_or_request_with_http_digest ブロックは、引数を username 一つだけ取ります。
そして、ブロックは password を返します。
authenticate_or_request_with_http_digest から false か nil を返すことは、認証失敗を引き起こします。

12 ストリーミングとファイルのダウンロード(Streaming and File Downloads)

場合によっては、 HTML ページをレンダリングする代わりにユーザーにファイルを送信したいかもしれません。
Rails のすべてのコントローラは、 snd_data と send_file メソッドを持っていて、どちらもクライアントにデータを配信するでしょう。
send_file は便利なメソッドです、ディスク上のファイルの名前を指定することができ、そのファイルの内容をストリーミングするでしょう。

クライアントにデータを配信するには send_data を使用します:

require "prawn"
class ClientsController < ApplicationController
  # クライアント上の情報から PDF 文書を生成し、それを返します。
  # ユーザーは ファイルとして PDF をダウンロード出来るでしょう。
  def download_pdf
    client = Client.find(params[:id])
    send_data(generate_pdf,
              :filename => "#{client.name}.pdf",
              :type => "application/pdf") 
  end

private

  def generate_pdf(client)
    Prawn::Document.new do
      text client.name, :align => :center
      text "Address: #{client.address}"
      text "Email: #{client.email}" 
    end.render
  end
end

上の例の中の download_pdf アクションは、実際に PDF 文書を作成し、それを文字列をして返す private メソッドを呼び出すでしょう。
この文字列は、クライアントにファイルダウンロードとしてストリーミングされ、ファイル名はユーザーに提案されるでしょう。
ユーザーにファイルをストリーミングする時、ファイルをダウンロードさせたくない時もあるでしょう。
例えば、 HTML ページに埋め込まれた画像とかです。
ブラウザにファイルはダウンロードするものではないと指示するには、 :disposition オプションを "inline" に設定できます。
その反対で且つ、このオプションのデフォルトの値は "attachment" です。

12.1 ファイルの送信(Sending Files)

ディスク上に既に存在するファイルを送信したい場合、 send_file メソッドを使用します。

class ClientsController < ApplicationController
  # 既に生成され、ディスクに保存されたファイルを配信する。
  def download_pdf
    client = Client.find(params[:id])
    send_data("#{RAILS_ROOT}/files/clients/#{client.id}.pdf",
              :filename => "#{client.name}.pdf",
              :type => "application/pdf") 
  end
end

これはファイルを読み込んで、メモリに一度にファイル全体を読み込むことを避けるためにファイルを 4KB ずつストリーミングするでしょう。
:stream オプションでストリーミングをオフにすることや、 :buffer_size オプションでブロックのサイズを調整することができます。

ディスク上のファイルを探すためにクライアントからの情報( params , cookies 他)を使う場合、
彼らが見れないはずのファイルにアクセスできる権限を誰かに与えるというセキュリティリスクがあることに注意して下さい。

ウェブ上のパブリックフォルダ内のサーバーにファイルを持てる場合、 Rails を介した静的ファイルの配信は推奨されません。
Apache や他の Web サーバーを使ってファイルを直接ダウンロード出来るようにするのは、
不必要な Rails 全体のスタックを通して行うよりも遥かに効率的です。
ですが、なんらかの理由によって Rails を通したリクエストが必要な場合、
:x_sendfile オプションを true に設定すれば、 Rails は、ユーザーにファイルを送信する処理を Web サーバーに制御させることができるようになり、
他の処理のための Rails のプロセスを空けることができます。
これが動作するには web サーバーが X-Sendfile ヘッダをサポートしている必要があることに注意して下さい。

12.2 RESTful なダウンロード(RESTful Downloads)

send_data が正常に動いている間、 RESTful なアプリケーションを作成していれば、
ファイルのダウンロードのための別のアクションを持つ必要は、通常ありません。
REST 的には、上の例の PDF ファイルはクライアントリソースのちょうど別の表現とみなされます。
Rails は、 "RESTful ダウンロード" を行うための非常に滑らかな簡単な方法を提供しています。
ここでは、例をどのように書き換えれば、 PDF ダウンロードをストリーミング無しに show アクションの一部にできるかを示しています:

class ClientsController < ApplicationController
  # ユーザーは HTML または PDF として、このリソースを受信するようにリクエストすることができます。 
  def show
    @client = Client.find(params[:id])

    respond_to do |format|
      format.html
      format.pdf { render :pdf => generate_pdf(@client) }
    end
  end
end

この例を実行するために、 Rails に PDF ファイルの MIME タイプを追加する必要があります。
config/initializers/mime_types.rb に次の行を追加することによって行うことができます。

Mime::Type.register "application/pdf", :pdf

設定ファイルは、各リクエスト上で、再読み込みされません。
なので、その変更を有効にするためにサーバーを再起動する必要があります。

今すぐユーザーは URL に ".pdf" を追加するだけで、クライアントの PDF バージョンを取得するようにリクエストできます。

GET /clients/1.pdf

13 パラメータフィルタ(Parameter Filtering)

Rails は log フォルダ内に、各環境用のログファイルを保持します。
これらはデバッグ時に何が実際にアプリケーションで起こっているか知るのに非常に便利です。
しかし活発なアプリケーションでは、ちょっとした情報は、ログファイルに格納したくない場合があります。
filter_parameter_logging メソッドは、ログから過敏な情報を排除するために使用することができます。
これは、ログが書き込まれた際に、 "[FILTERED]" で params ハッシュ内の特定の値を置き換えることで動作します。
例として、 "password" を含むキーですべてのパラメータをどのようにフィルタリングするかを見てみましょう:

class ApplicationController < ActionController::Base
  filter_parameter_logging :password
end

メソッドは params ハッシュの全てのレベルを通じて再帰的に動作し、
置換文字列が存在する場合、オプションの第2パラメータを取ります。
それは、順番にそれぞれのキーを受け取るブロックを受けとり、それらを置き換えてブロックが true を返します。

14 rescue(Rescue)

ほとんどのアプリケーションはバグが含まれている可能性が高く、そうでなければ処理する必要がある例外をスローします。
例えばユーザーが、データベースにもはや存在しないリソースへのリンクを参照している場合、
Active Record は、 ActiveRecord::RecordNotFound 例外をスローします。

Rails のデフォルトの例外処理は、全ての例外に対して "500 Server Error" を表示します。
リクエストがローカルで行われた場合、素敵なトレースバックと、追加情報が表示されるので、何が悪いか、何に対処するかを理解できます。
ルーティングエラーやレコードが見つからなかった場合、
リクエストがリモートなら Rails は単に "500 Server Error" か "404 Not Found" を表示するでしょう。
これらのエラーのキャッチ方法と、ユーザーへの表示方法をカスタマイズしたいかもしれません。
Rails アプリケーションでは、利用可能ないくつかのレベルの例外処理があります。

14.1 デフォルトの 500 、 404 テンプレート(The Default 500 and 404 Templates)

デフォルトでは、 production アプリケーションは、 404 か 500 エラーメッセージをレンダリングするでしょう。
これらのメッセージは、 public フォルダの中の静的 HTML である 404.html や 500.html の中にそれぞれ、含まれています。
他の情報やレイアウトを追加するためにこれらのファイルをカスタマイズできますが、
これらのファイルは静的であることを覚えていて下さい、すなわち、 RHTML や layouts を使うことは出来ず、プレーンな HTML だけです。

14.2 rescue_from(rescue_from)

エラーをキャッチするとき、もう少し手の込んだことをしたいなら、 rescue_from を使えます。
それはコントローラ全体とそのサブクラスの中で特定タイプ(もしくは複数のタイプ)の例外だけを処理します。

例外が発生して rescue_from ディレクティブでキャッチされた時、例外オブジェクトはハンドラに渡されます。
ハンドラは、メソッドか Proc オブジェクトを :with オプションに渡せます。
明示的な Proc オブジェクトの代わりに直接ブロックを使うことも出来ます。

ここに、全ての ActiveRecord::RecordNotFound とそれに付随するものをインターセプトするための、 rescue_from の使い方があります。

class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found

  private

  def record_not_found
    render :text => "404 Not Found", :status => 404
  end
end

もちろん、この例は決して手の込んだものではなく、すべてのデフォルトの例外処理を改善しません。
が、一度に、欲しいすべての例外をキャッチすることができます。
例えば、ユーザがアプリケーションの特定のセクションへのアクセスを持っていない時に、
スローされるカスタム例外クラスを作成すること出来ます。

class ApplicationController < ActionController::Base
  rescue_from User::NotAuthorized, :with => :user_not_authorized

  private
  def user_not_authorized
    flash[:error] = "You don't have access to this section."
    redirect_to :back
  end
end

class ClientsController < ApplicationController
  # ユーザーが顧客にアクセスする正しい権限を持っているか確認します。
  before_filter :check_authorization 

  # 全ての認証についてアクションが心配する必要がないことに注意して下さい。
  def edit
    @client = Client.find(params[:id]) 
  end

private
  # ユーザーは、承認されていない場合、ただ例外をスローします。
  def check_authorization
    raise User::NotAuthorized unless current_user.admin?
  end
end 

コントローラが初期化されアクションが実行される前に raise するような特定の例外は
ApplicationController クラスでだけ rescue できます。
この議論についてもっと多くの情報のために Pratik Naik の記事を見てください。

15 Changelog

Lighthouse ticket

  • February 17, 2009: Yet another proofread by Xavier Noria.
  • November 4, 2008: First release version by Tore Darell

コメント欄(誤訳・誤字があれば教えて頂ければ幸いです。)