88 lines
2.9 KiB
Markdown
88 lines
2.9 KiB
Markdown
|
+++
|
||
|
date = "2011-10-14"
|
||
|
title = "Rails 3: Customized exception handling"
|
||
|
tags = ["Rails", "rails3", "exceptions", "error", "rescue"]
|
||
|
slug = "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
|
||
|
|
||
|
|