devroom.io/content/posts/2011-10-14-rails-3-customized-exception-handling.md
2019-06-05 14:32:16 +02:00

101 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:
``` ruby
# 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:
``` ruby
# 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:
``` ruby
# GET /p/:name
def show
@profile = Profile.find(params[:name])
end
```
Then, in your `app/controllers/application_controller.rb` add this:
``` ruby
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:
``` ruby
# 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`:
``` ruby
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:
``` ruby
rescue_from MyApp::ProfileNotFoundError do |exception|
render :nothing => "Profile not found.", :status => 404
end
```