2012-04-20 76 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