使用Rails 4.0进行自定义error handling

我正在使用Ruby 2.0和Rails 4.0构建Ruby on Rails api。 我的应用程序几乎只是一个JSON API,所以如果发生错误(500,404),我想捕获该错误并返回格式良好的JSON错误消息。

我试过这个并且还:

rescue_from ActionController::RoutingError, :with => :error_render_method def error_render_method puts "HANDLING ERROR" render :json => { :errors => "Method not found." }, :status => :not_found true end 

在我的ApplicationController中。

这些都没有做到这一点(根本没有捕获例外)。 我的谷歌搜索显示这在3.1,3.2之间发生了很大的变化,我找不到任何关于如何在Rails 4.0中做到这一点的好文档。

有人知道吗?

编辑这是我进入404页面时的堆栈跟踪:

 Started GET "/testing" for 127.0.0.1 at 2013-08-21 09:50:42 -0400 ActionController::RoutingError (No route matches [GET] "/testing"): actionpack (4.0.0) lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call' actionpack (4.0.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call' railties (4.0.0) lib/rails/rack/logger.rb:38:in `call_app' railties (4.0.0) lib/rails/rack/logger.rb:21:in `block in call' activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `block in tagged' activesupport (4.0.0) lib/active_support/tagged_logging.rb:25:in `tagged' activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `tagged' railties (4.0.0) lib/rails/rack/logger.rb:21:in `call' actionpack (4.0.0) lib/action_dispatch/middleware/request_id.rb:21:in `call' rack (1.5.2) lib/rack/methodoverride.rb:21:in `call' rack (1.5.2) lib/rack/runtime.rb:17:in `call' activesupport (4.0.0) lib/active_support/cache/strategy/local_cache.rb:83:in `call' rack (1.5.2) lib/rack/lock.rb:17:in `call' actionpack (4.0.0) lib/action_dispatch/middleware/static.rb:64:in `call' railties (4.0.0) lib/rails/engine.rb:511:in `call' railties (4.0.0) lib/rails/application.rb:97:in `call' rack (1.5.2) lib/rack/lock.rb:17:in `call' rack (1.5.2) lib/rack/content_length.rb:14:in `call' rack (1.5.2) lib/rack/handler/webrick.rb:60:in `service' /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service' /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run' /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/webrick/server.rb:295:in `block in start_thread' Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms) Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/routes/_route.html.erb (2.9ms) Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/routes/_route.html.erb (0.9ms) Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/routes/_table.html.erb (1.1ms) Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (38.3ms) 

我不认为我希望它能够做到这一点,有些东西应该抓住它并返回适当的json错误响应。

该请求甚至没有打到您的应用程序。

您需要定义一个catchall路由,以便Rails将请求发送到您的应用程序而不是显示错误(在开发中)或呈现public / 404.html页面(在生产中)

修改routes.rb文件以包含以下内容

 match "*path", to: "errors#catch_404", via: :all 

在你的控制器

 class ErrorsController < ApplicationController def catch_404 raise ActionController::RoutingError.new(params[:path]) end end 

然后你的rescue_from应该抓住错误。

在尝试了一些变化之后,我已经确定这是处理API 404s的最简单方法:

 # Passing request spec describe 'making a request to an unrecognised path' do before { host! 'api.example.com' } it 'returns 404' do get '/nowhere' expect(response.status).to eq(404) end end # routing constraints subdomain: 'api' do namespace :api, path: '', defaults: { format: 'json' } do scope module: :v1, constraints: ApiConstraints.new(1) do # ... actual routes omitted ... end match "*path", to: -> (env) { [404, {}, ['{"error": "not_found"}']] }, via: :all end end 

这适用于rails4,这样你就可以直接管理所有错误:例如,当api调用发生错误时,你可以将error_info渲染为json。

application_controller.rb

 class ApplicationController < ActionController::Base protect_from_forgery # CUSTOM EXCEPTION HANDLING rescue_from StandardError do |e| error(e) end def routing_error raise ActionController::RoutingError.new(params[:path]) end protected def error(e) #render :template => "#{Rails::root}/public/404.html" if env["ORIGINAL_FULLPATH"] =~ /^\/api/ error_info = { :error => "internal-server-error", :exception => "#{e.class.name} : #{e.message}", } error_info[:trace] = e.backtrace[0,10] if Rails.env.development? render :json => error_info.to_json, :status => 500 else #render :text => "500 Internal Server Error", :status => 500 # You can render your own template here raise e end end # ... end 

的routes.rb

 MyApp::Application.routes.draw do # ... # Any other routes are handled here (as ActionDispatch prevents RoutingError from hitting ApplicationController::rescue_action). match "*path", :to => "application#routing_error", :via => :all end 

我在公共文件夹中使用了404.html,这是在开发环境中。
我实际上得到了答案:

但是,我做了一些关于实际使其工作的代码片段的实验。 这是我刚才添加的代码片段。

配置/ routes.rb中

 Rails.application.routes.draw do // other routes match "*path", to: "application#catch_404", via: :all end 

应用程序/控制器/ application_controller.rb

 class ApplicationController < ActionController::Base def catch_404 render :file => 'public/404.html', :status => :not_found end end 

我们将不胜感激任何评论和澄清,为什么需要一些原件。 例如,使用这行代码

 raise ActionController::RoutingError.new(params[:path]) 

还有这个

 rescue_from ActionController::RoutingError, :with => :error_render_method 

因为rescue_fromraise ActionController::RoutingError似乎是旧Rails版本的流行答案。

如果您想以相同的方式响应所有类型的错误,请尝试此操作

rescue_from StandardError, :with => :error_render_method

如果您不希望在开发模式下出现此行为,请在下面添加上面的代码

unless Rails.application.config.consider_all_requests_local