: O. Yuanying

RecordNotFound で 404 Not found

Scaffold されたコード

Rails で scaffold したばかりのソースコードでは、あるモデルを表示するためのアクション (show) は以下のように記述されている。


  # GET /series/1
  # GET /series/1.xml
  def show
    @series = Series.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @series }
    end
  end

この場合、params[:id] が存在しない値だった場合、ActiveRecord::RecordNotFound が発生する。

development 環境だった場合、以下のような stacktrace が表示されるだろう。

RecordNotfound.jpg

これが production 環境だった場合、 Module: ActionController::Rescue#rescue_action で補足されて404 Not Found が表示される。

ソースコード改悪後

アプリケーションを開発していくと scaffold されたソースコードがそのまま残ることは多くない。 例えば指定されたモデルがあるユーザに確実に所有してされていることを保証したいと考えて、 以下のようにコードを改変したとする。


  # GET /series/1
  # GET /series/1.xml
  def show
-   @series = Series.find(params[:id])
+   @series = Series.find_by_id_and_user_id(params[:id], current_user.id)

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @series }
    end
  end

この場合、該当するモデルが存在しなかった場合でも ActiveRecord::RecordNotFound は発生せず、 @series は nil になる。

相当運が良くない限り、このソースコードを production 環境で運用すれば、 該当するモデルが存在しなかった場合、404 Not Found ではなく、5xx Application Error と表示されてしまう。

ソースコードの修正

これはあまり格好のいい話ではないので、 このような場合は nil をチェックして自分で ActiveRecord::RecordNotFound を発生させるべきだと思う。


  # GET /series/1
  # GET /series/1.xml
  def show
-   @series = Series.find(params[:id])
+   @series = Series.find_by_id_and_user_id(params[:id], current_user.id)
+   raise ActiveRecord::RecordNotFound if @series.nil?

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @series }
    end
  end