2012-04-20 29 views
69

Railsアプリケーションで通常のHTMLフロントエンドとJSON APIを使用しています。今、誰かが/api/not_existent_method.jsonを呼び出すと、デフォルトのHTML 404ページを返します。 {"error": "not_found"}のようなものに変更する方法はありますか?元の404ページをそのままHTMLフロントエンドに残しておきますか?RailsでJSON形式の404エラーを返す必要があります

class ApplicationController < ActionController::Base 
    rescue_from NotFoundException, :with => :not_found 
    ... 

    def not_found 
    respond_to do |format| 
     format.html { render :file => File.join(Rails.root, 'public', '404.html') } 
     format.json { render :text => '{"error": "not_found"}' } 
    end 
    end 
end 

NotFoundException例外の本当の名前ではありません。

答えて

103

友人が404を取り扱うだけでなく、500エラーが発生するだけでなく、エレガントな解決に向けて、私を指摘しました。実際、すべてのエラーを処理します。重要なのは、すべてのエラーが例外を生成し、ラックミドルウェアのスタックを介して上方に伝播し、そのいずれかによって処理されるまでです。詳細を知りたい場合は、this excellent screencastをご覧ください。 Railsには例外のハンドラがありますが、文書化されていないexceptions_app設定オプションで上書きできます。

# In your config/application.rb 
config.exceptions_app = self.routes 

その後、あなたは自分のconfig/routes.rbにこれらのルートを照合する必要があります:その後、

get "/404" => "errors#not_found" 
get "/500" => "errors#exception" 

そして、あなたさて、あなたはこのように、バックレールにルートエラーをすることができ、独自のミドルウェアを書いたりすることができますこれを処理するコントローラを作成するだけです。

class ErrorsController < ActionController::Base 
    def not_found 
    if env["REQUEST_PATH"] =~ /^\/api/ 
     render :json => {:error => "not-found"}.to_json, :status => 404 
    else 
     render :text => "404 Not found", :status => 404 # You can render your own template here 
    end 
    end 

    def exception 
    if env["REQUEST_PATH"] =~ /^\/api/ 
     render :json => {:error => "internal-server-error"}.to_json, :status => 500 
    else 
     render :text => "500 Internal Server Error", :status => 500 # You can render your own template here 
    end 
    end 
end 

最後に一つ追加する:開発環境では、404または500ページをレンダリングしませんusallyレール代わりにバックトレースを出力します。 ErrorsControllerが開発モードで動作していることを確認するには、config/enviroments/development.rbファイルのバックトレースの内容を無効にします。

config.consider_all_requests_local = false 
+4

また、ステータスコードをレンダラーに追加することを忘れないでください。そうしないと、クライアント/ブラウザは404/500であることを知りません。 レンダリング:text => "404 not found"、:ステータス=>:not_found – arnab

+0

私は私の答えを編集しました。 – iblue

+1

私はrespond_toブロックがレンダリング機能でより普遍的だと言うでしょう: respond_to do | format | format.json {エラー: "not-found"}。to_json、status:404} format.html {レンダリングテキスト: "404 Not found"、ステータス:404} end –

11

確かに、それは次のようになります。これは、Railsのバージョンとあなたが望む正確な動作によって変わります。 Googleの検索で簡単に見つけることができます。

+2

考えためのおかげで、しかし例外処理が変更されたRails 3.2.2を使用しています。これはもはや機能しません。 – iblue

+0

@iblueこれはRails 3.2以降でうまく動作し、より良い解決策です。詳細は、 'rescue_from' [docs](http://apidock.com/rails/v3.2.3/ActiveSupport/Rescuable/ClassMethods/rescue_from)を参照してください。 – Intelekshual

+0

これは404エラーでのみ動作し、500エラーでは動作しません。 – iblue

4

終わり、あなたのroutes.rbのでを配置しようとすると:

match '*foo', :format => true, :constraints => {:format => :json}, :to => lambda {|env| [404, {}, ['{"error": "not_found"}']] } 
+0

これを読んでいる人は、 'Rails.application.routes'ファイルの' match'は 'via:'パラメータを使ってHTTPメソッドを指定する必要があります。上記の行を使用しようとすると、これもエラーとして表示されます。 – sameers

13

Iの形式を設定する別のAPIコントローラ(JSON)を作成したいとAPI固有のメソッド:

class ApiController < ApplicationController 
    respond_to :json 

    rescue_from ActiveRecord::RecordNotFound, with: :not_found 
    # Use Mongoid::Errors::DocumentNotFound with mongoid 

    def not_found 
    respond_with '{"error": "not_found"}', status: :not_found 
    end 
end 

RSpecのテスト:

it 'should return 404' do 
    get "/api/route/specific/to/your/app/", format: :json 
    expect(response.status).to eq(404) 
    end 
+0

これはレコード専用です。 'api/non_existant_route'のケースをどうやって管理しますか? – Sebastialonso

+0

'rescue_from ActiveRecord :: RecordNotFound、with:not_found'行は' with::not_found'にする必要がありますが、これは1文字の編集です:P – erroric

+0

@erroric thanks! :) –

関連する問題