I spent a couple of hours trying to figure out how to handle 404 and 500 errors in Rails. This is not simple and actually really annoying. Hopefully future versions clean this up because right now it sucks pretty badly. Anyways, I found a page on the wiki and some other blogs, but the issue was that they wouldn’t handle all the cases. So, here’s the solution:
1. Edit your app/controllers/application.rb file and add these three methods:
def rescue_404
rescue_action_in_public CustomNotFoundError.new
end
def rescue_action_in_public(exception)
case exception
when CustomNotFoundError, ::ActionController::UnknownAction then
#render_with_layout "shared/error404", 404, "standard"
render :template => "shared/error404", :layout => "standard", :status => "404"
else
@message = exception
render :template => "shared/error", :layout => "standard", :status => "500"
end
end
def local_request?
return false
end
The first method will be explained in the next step. The second method is the method that Rails calls to handle most errors. This method will not capture a certain class of errors where neither the controller nor the action requested exist. The third method tells rails to stop sucking. Normally Rails handles requests made to localhost or 127.0.0.1 differently than all others. This might be for debugging purposes, but it sucks when testing error handling.
2. Edit config/routes.rb and add this line TO THE END OF THE FILE:
map.connect '*path', :controller => 'application', :action => 'rescue_404' unless ::ActionController::Base.consider_all_requests_local
This tells Rails that if it can’t find any other route to handle the request (i.e. the *path) it should call the rescue_404 action on the application controller (the first method above).
3. Edit config/environments/development.rb and add this line:
ActionController::Base.consider_all_requests_local = false
This additionally tells Rails to stop sucking and stop handling requests to localhost and 127.0.0.1 differently.
Anyways, happy coding.
map.connect “*anything”, :controller => “your_controller”, :action => “your_action”
params[:anything]
http://henrik.nyh.se/2008/07/rails-404
http://6brand.com/rails-custom-404-pages.html
Rails全局处理Error
查看Rails源码 :actionpack/lib/action_controller/rescue.rb:
def rescue_action_in_public(exception) #:doc:
render_optional_error_file response_code_for_rescue(exception)
end
我们可以重写这个方法,application.rb中:
def rescue_action_in_public(exception)
case exception.class.name
when
'ActiveRecord::RecordNotFound','::ActionController::UnknownAction','::ActionController::RoutingError'
RAILS_DEFAULT_LOGGER.error("404 displayed")
render(:file => "#{RAILS_ROOT}/public/404.html", :status => "404 Error")
else
RAILS_DEFAULT_LOGGER.error("500 displayed")
render(:file => "#{RAILS_ROOT}/public/500.html", :status => "500 Error")
end
end
然后注意,这里rescue_action_in_public方法是只对production环境有用的,在developmen模式下不起作用
我们继续看源码:
def rescue_action(exception)
log_error(exception) if logger
erase_results if performed?
# Let the exception alter the response if it wants.
# For example, MethodNotAllowed sets the Allow header.
if exception.respond_to?(:handle_response!)
exception.handle_response!(response)
end
if consider_all_requests_local || local_request?
rescue_action_locally(exception)
else
rescue_action_in_public(exception)
end
end
注 意最后那个if语句,这里是判断request是否来自本地的请求,当为development下,被rescue_action_locally方法处 理exception,但是我们如果在application里重写rescue_action_locally方法,就可以自由的测试开发了。如下:
#for development
def rescue_action_locally(exception)
case exception.class.name
when 'ActiveRecord::RecordNotFound','::ActionController::UnknownAction','::ActionController::RoutingError'
RAILS_DEFAULT_LOGGER.error("404 displayed")
render(:file => "#{RAILS_ROOT}/public/404.html", :status => "404 Error")
else
RAILS_DEFAULT_LOGGER.error("500 displayed")
render(:file => "#{RAILS_ROOT}/public/500.html", :status => "500 Error")
end
end
rails出错的时候,一般都是跳转到错误跟踪的页面下,这样在开发环境下是很好的,但是对正式环境就不是太友好了,所以如果你想自定义一下出错的页面,还是有办法的.
我一般都会在lib目录下新建一个ooxx.rb文件存放下面的代码,然后在environment.rb下加载一下.
environment.rb
require File .join( File .dirname(__FILE__), '..' , 'lib' , 'ooxx' )
require File.join(File.dirname(__FILE__),'..', 'lib','ooxx')
ooxx.rb
module ActionController
module Rescue
protected
#自定义的错误处理
def rescue_action_in_public(exception)
logger.error("rescue_action_in_public executed")
# 这里就是你可以折腾的地方
case exception
when ActiveRecord::RecordNotFound, ::ActionController::RoutingError,
::ActionController::UnknownAction
logger.error("404 displayed")
render(:file => "#{RAILS_ROOT}/public/404.html",
:status => "404 Not Found")
else
logger.error("500 displayed")
render(:file => "#{RAILS_ROOT}/public/500.html",
:status => "500 Error")
# SystemNotifier.deliver_exception_notification(self, request,
# exception)
end
end
end
end
请注意做完以上操作后,在开发环境下,照样会跳转到错误跟踪页面的,这是因为这个方法是否起作用取决于development.rb和production.rb中的配置:
# in production.rb
config.action_controller.consider_all_requests_local = false
# in development.rb
config.action_controller.consider_all_requests_local = true