ActionController::RoutingError的rescue_from无效

22
我正在尝试从ActionController :: RoutingError中进行救援,但我无法让它起作用。我几乎已经尝试了所有可以在网上找到的方法,包括在Rails 4中从ActionController :: RoutingError中进行救援。我有一个错误控制器和错误页面。我已经解决了cancan中的access deniedRecordNotFound问题,但我无法解决RoutingError

对于cancan,我在application_controller.rb中使用以下代码:

rescue_from CanCan::AccessDenied do
    render template: 'errors/error_403', status: 403
  end

而我在我的路由中有这个:

match "/404", to: "errors#error_404", via: :all

如果我对RoutingError做同样的事情,它不会起作用。

我也尝试过match '*path',:to => "errors#error_404"但是我收到错误信息。

我该如何解决?

编辑:如果我对访问被拒绝和RoutingError做同样的事情:

    rescue_from ActionController::RoutingError do
       render template: 'errors/error_404', status: 404
    end

它不会工作。

2个回答

47

ActionController::RoutingError 是当 Rails 尝试将请求与路由匹配时抛出的错误。这发生在 Rails 初始化控制器之前 - 因此您的 ApplicationController 没有机会拯救异常。

相反,Rails 默认的 exceptions_app 会启动 - 请注意,这是 Rack 中的应用程序,它使用带有请求的 ENV 哈希并返回响应 - 在这种情况下是静态的 /public/404.html 文件。

您可以让您的 Rails 应用程序动态处理渲染错误页面:

# config/application.rb
config.exceptions_app = self.routes # a Rack Application

# config/routes.rb
match "/404", :to => "errors#not_found", :via => :all
match "/500", :to => "errors#internal_server_error", :via => :all

那么您将设置一个特定的控制器来处理错误页面——不要在您的 ApplicationController 类中这样做,因为您会向所有控制器添加 not_foundinternal_server_error 方法!

class ErrorsController < ActionController::Base
  protect_from_forgery with: :null_session

  def not_found
    render(status: 404)
  end

  def internal_server_error
    render(status: 500)
  end
end

代码借鉴自Matt Brictson: Dynamic Rails Error Pages - 详细了解请查看原文。


非常感谢您提供如此详尽的答案。我现在理解得更好了。 - Bogdan Daniel
感谢您的解释,即在Rails实际调用应用程序之前,ActionController :: RoutingError将引发。我尝试遵循您上面的建议,但是当我尝试访问像localhost:3000 / xxxx这样的网址时,仍然会出现ActionController :: RoutingError(没有匹配路由[GET]“/xxxx”)。也许我漏掉了什么? - worrawut
7
我的错。我需要告诉Rails通过在config/environment/development.rb中添加这一行代码config.consider_all_requests_local = false来渲染错误页面。现在一切都正常了。 - worrawut
1
这个解决方案在使用nginx和passenger的Rails生产模式中不起作用,在Ruby 2.3.8中也是如此。 - vidur punj

21

有一种更好的方法:

routes.rb

Rails.application.routes.draw do
  match '*unmatched', to: 'application#route_not_found', via: :all
end

应用控制器 application_controller.rb

class ApplicationController < ActionController::Base
  def route_not_found
    render file: Rails.public_path.join('404.html'), status: :not_found, layout: false
  end
end

要在本地进行测试,请设置以下内容并重新启动服务器。

config/development.rb

config.consider_all_requests_local = false

使用Rails 6进行测试。


我刚在Rails 7中测试了这个,只需更改开发配置就足够了。 - Carla Urrea Stabile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接