Rails 3: Customized exception handling
Exceptions happen. There’s no way around that. But not all exceptions are created equally.
For instance, a 404 “Not found” error can (and should) be handled correctly in your application.
Let me give you an example of how to handle a ActiveRecord::RecordNotFound
exception. Let’s assume you have an application that could show a user profile:
# GET /p/:name
def show
@profile = Profile.find(params[:name])
end
Now, it may happen that the :name
paramater contains a value that cannot be found in our database, most likely because someone made a typo in the URL.
If Profile#find
cannot get a proper result it will throw ActiveRecord::RecordNotFound
.
Now, instead of showing the user the (by default ugly) 404 page from public/404.html
we want to do something more fancy.
Action-specific exception handling
Here’s one solution:
# GET /p/:name
def show
@profile = Profile.find(params[:name])
rescue
render :template => 'application/profile_not_found', :status => :not_found
end
You can now create app/views/applicaiton/profile_not_found.html.haml
and give a nice custom error message to your user.
You may try to find some matching profiles to :name
or show a search box.
Global exception handling
The above example only works for the specific profile show
action. It’s also possible to hanlde exceptions on the application level.
Your show
action still looks like this:
# GET /p/:name
def show
@profile = Profile.find(params[:name])
end
Then, in your app/controllers/application_controller.rb
add this:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :rescue_not_found
protected
def rescue_not_found
render :template => 'application/not_found', :status => :not_found
end
end
Whenever an ActiveRecord::RecordNotFound
exception is thrown (and not handled by the action itself), it will be handled by your ApplicationController
.
Custom exceptions
It’s possible to throw your own custom exceptions and handle them in different ways. Like this:
# Define your own error
class MyApp::ProfileNotFoundError < StandardError
end
# GET /p/:name
def show
@profile = Profile.find_by_name(params[:name])
raise MyApp::ProfileNotFoundError if @profile.nil?
end
And add this to your ApplicationController
:
rescue_from MyApp::ProfileNotFoundError, :with => :profile_not_found
Optionally, if you don’t want to write that custom profile_not_found
method, you may also supply a block:
rescue_from MyApp::ProfileNotFoundError do |exception|
render :nothing => "Profile not found.", :status => 404
end