2012-04-20 49 views
69

我的Rails應用程序中有一個正常的HTML前端和一個JSON API。現在,如果有人撥打/api/not_existent_method.json,它將返回默認的HTML 404頁面。有什麼方法可以將此改爲{"error": "not_found"}之類的東西,同時保留HTML前端的原始404頁面完好嗎?需要在Rails中返回JSON格式的404錯誤

回答

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 

要添加的最後一件事:在開發環境中,rails通常不呈現404或500頁,而是打印回溯。如果您想在開發模式下看到您的ErrorsController正在運行,請在config/enviroments/development.rb文件中禁用回溯功能。

config.consider_all_requests_local = false 
+4

另外,不要忘記添加狀態碼到渲染。否則你的客戶端/瀏覽器不會知道它是一個404/500。 render:text =>「404 not found」,:status =>:not_found – arnab 2012-08-03 04:46:54

+0

我編輯了我的答案。 – iblue 2012-08-03 10:02:34

+1

我會說一個respond_to塊在渲染函數中更普遍: respond_to do | format | format.json {render json:{error:「not-found」}。to_json,status:404} format.html {render text:「404 Not found」,status:404} end – 2013-04-19 16:11:11

11

當然,這將是這個樣子:

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異常的真實姓名。它會隨着Rails版本和你想要的確切行爲而變化。使用Google搜索很容易找到。

+2

感謝您的想法,但我正在使用Rails 3.2.2,其中異常處理髮生了變化。這將不再有效。 – iblue 2012-04-20 21:38:15

+0

@iblue這在Rails 3.2+中工作得很好,是更好的解決方案。有關更多信息,請參閱'rescue_from' [docs](http://apidock.com/rails/v3.2.3/ActiveSupport/Rescuable/ClassMethods/rescue_from)。 – Intelekshual 2012-08-07 17:42:00

+0

它只適用於404錯誤,而不適用於500錯誤。 – iblue 2012-08-07 18:38:21

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 2017-07-22 18:32:58

13

我喜歡創建一個單獨的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 2015-02-22 14:05:50

+0

'rescue_from ActiveRecord :: RecordNotFound,with:not_found'需要'with::not_found',但它只是一個字符編輯:P – erroric 2015-05-08 20:03:17

+0

@erroric thanks! :) – 2015-05-10 17:57:39