Building Custom Error Pages
A good error page is important in the user experience of a website. Lucky for us Ruby on Rails provides a convenient way to update any 404 (Not Found), 422 (Unprocessable Entity), and 500 (Internal Server Error) errors that occur in your application.
This is easily done by updating the files located in your Rails root at:
/public/404.html/public/422.html/public/500.html
But sadly, it's not good enough.
The Problems
The problem with is that it simply isn't flexible enough:
- Unable to embed ruby code.
- Requires duplicating layout code.
- Dynamic code in layout is made static in error pages.
The Solution
Inside the ActionPack code, located in /lib/action_controller/rescue.rb
is the method rescue_action_in_public.
Lets have a look at the comment:
Overwrite to implement public exception handling (for requests answering false to local_request?). By default will call render_optional_error_file. Override this method to provide more user friendly error messages.
Bingo! Exactly what we wanted.
Firstly lets generate he simple 404, 422, and 500 error pages. Likewise I leave them in their own directory in the /app/views
folder, I like to call this directory "errors".
Hence my new files will be located like so:
/app/view/errors/404.html.erb/app/view/errors/422.html.erb/app/view/errors/500.html.erb
Now feel free to remove the error pages in your /public
directory.
Now in our ApplicationController (see /app/controllers/application.rb
), lets add override the function rescue_optional_error_file,
class ApplicationController < ActionController::Base
...
protected
def render_optional_error_file(status_code)
status = interpret_status(status_code)
render :template => "/errors/#{status[0,3]}.html.erb", :status => status, :layout => 'application.html.erb'
end
...
end
Note: I have to explicitly state the file extension and layout, else when an unknown format is requested
Testing
Off the bat, you cannot test this on your local machine. You are required to redefine the local_request? in the ApplicationController, like so:
class ApplicationController < ActionController::Base
...
protected
def local_request?
true
end
...
end
Now you are ready for testing out your new error pages!
Note: Don't forget to remove the local_request? when done!

October 4th, 2009 - 07:17
I think you have typo there. The method you should be overriding is
def rescue_optional_error_file(status_code) not
def rescue_optional_error_file(status_code)
This is based on the following documentation http://api.rubyonrails.org/classes/ActionController/Rescue.html
Also def local_request? should return false not true for it to work in your development machine.
October 4th, 2009 - 07:18
I think you have typo there. The method you should be overriding is
def render_optional_error_file(status_code) not
def rescue_optional_error_file(status_code)
This is based on the following documentation http://api.rubyonrails.org/classes/ActionController/Rescue.html
Also def local_request? should return false not true for it to work in your development machine.
July 22nd, 2010 - 03:44
I changed to :
def local_request?
false
end
And works like a charm. Thanks!