【问题标题】:Custom Error Handling with Rails 4.0Rails 4.0 的自定义错误处理
【发布时间】:2013-08-23 22:09:17
【问题描述】:

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

我试过this 并且还:

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 错误响应。

【问题讨论】:

  • 嗯,这实际上可能是我设置它的方式......让我再测试一下。

标签: ruby-on-rails json ruby-on-rails-4 ruby-2.0 actiondispatch


【解决方案1】:

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

不过,我做了一个小实验,看看是哪些代码段让它真正起作用。这是我只添加的代码片段。

config/routes.rb

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

app/controllers/application_controller.rb

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

将感谢任何 cmets 并说明为什么需要一些原始文件。比如使用这行代码

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

还有这个

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

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

【讨论】:

    【解决方案2】:

    在尝试了一些变体之后,我认为这是处理 API 404 的最简单方法:

    # 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
    

    【讨论】:

    • +1 用于将其保存在路由文件中并添加测试。我在我的应用程序中或多或少地使用了这个,谢谢!
    【解决方案3】:

    该请求甚至没有命中您的应用。

    您需要定义一个包罗万象的路由,以便 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 应该会发现错误。

    【讨论】:

    • 您可能希望将路由中的get 更改为match,因为其他请求方法也可能发生这种情况。
    • Rails 4 会提醒您“将您的操作暴露给 GET 和 POST,[by] add[ing] via: [:get, :post]” - 记得也要这样做! :)
    【解决方案4】:

    这适用于 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
    

    【讨论】:

      【解决方案5】:

      如果你想以同样的方式响应所有类型的错误,试试这个

      rescue_from StandardError, :with =&gt; :error_render_method

      如果您不希望在您的开发模式下出现这种行为,请在

      下添加上述代码

      unless Rails.application.config.consider_all_requests_local

      【讨论】:

      • 很高兴知道,但看起来这根本没有捕捉到错误(我添加了一个堆栈跟踪)。我的“error_render_method”永远不会被调用。
      • 我使用的是 rails 3.2.13,它对我有用。也许他们在 Rails 4 中改变了一些东西
      • 如果您错误地将其包含在 ActionController 中,则在少数不同情况下不会调用它。来自文档:“exception.is_a?(klass) 成立的第一个类的处理程序是被调用的处理程序,如果有的话”
      猜你喜欢
      • 2016-06-02
      • 2018-10-12
      • 2013-02-12
      • 2011-01-29
      • 2010-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-19
      相关资源
      最近更新 更多