Error handling in RESTful applications
Lately, many web frameworks began to provide RESTful routing. More over, REST became a design standard for a web application architecture. Today each more or less known service has RESTful API and allows an interaction by xml or json responses.
Despite the fact that there are lot manuals for software developers about REST design the problem of REST error return is discussed not enough. Further REST is considered oftenly as a set of routing rules and all what is not directly related with web routing is solved in an arbitrary way.
The article is about approaches in solving problem of error return in RESTful applications.
Error handling on software level
Consider a web application which an user can add an article
to article list self.article
. Commonly such approch is used:
- Collecting of data fields from a form and creating a object which incapsulated this data.
- The data are sending through the post/put request to an server.
- Further executing of a client-code depends on a response status variable, e.g.
response.status == "ok"
It may be look like that:
$.post('articles', {title: 'title', text: 'text'}, 'json').done(function(data){
if(response.status == 'ok'){
self.articles.push(data);
}else{
var messages = response.messages;
// Handle error and show message
}
}).fail(function(response){
// Handle server or connection error
});
And near is a example of controller wrote for Ruby on Rails. This controller is not best practic but many peoples write same code. The result of executing of method create
does not affect on HTTP status code:
class ArticlesController < ApplicationController
def create
@article = Article.new
@article.title = params[:title]
@article.text = params[:text]
# ... some actions ...
if not @article.valid?
render json: {status: 'error', messages: @article.errors.messages}
end
@article.save
render json: @article
end
end
Pros:
- The error object may to care additional information.
- Count of states is not limited.
- Knowing of HTTP niceties is not required.
Cons:
- Response semantic is broken. Invalid request may returns HTTP status code '200 OK'.
- Application and software levels duplicates each other. The client code ought to validate both results the XMLHttpRequest response and the custom error object state.
Erorr handling on application level
Because RESTful already makes some limitations so such approach may be used for error handling. Just as web resources reflect domain models of service so HTTP status codes can reflect information about errors.
E.g. the status code 422 Unprocessable Entity
can be used for notification about incorrect form data. In such case the response body may contains an array of incorrect fields.
$.post(
'articles', {title: 'title', text: 'text'}, 'json'
).done(function(response){
var article = response.data;
self.articles.push(article);
}).fail(function(e, ){
switch(e.status){
case 422:
var messages = response.responseText;
// Handle validation error and show message
default:
// Handle server or connection error
}
});
On the server side there is a sense to divide error handling on different rescue-blocks. So in case of form data with incorrect fields the first rescue block will be called. In case of another erors with correct data fields the last rescue-block will be called. Thus on client side the erorrs can be returned in response without additional status fields in json.
class ArticlesController < ApplicationController
def create
@article = Article.new
@article.title = params[:title]
@article.text = params[:text]
# ... some actions ...
# ! makes exceptions on incorrect fields
@article.save!
render json: @article
rescue Mongoid::Errors::Validations
render json: @article.errors.messages, status: 422
rescue
render text: 'Internal server error', status: 500
end
end
Pros:
- Semantics of HTTP and application status codes coincide like models coincide with resource names and methods of controllers with types of HTTP requests.
- Duplication of application and software levels is excluded.
- A specification of error statuses is not required.
Cons:
- Not always needed status code exist.
- Good knowing of HTTP niceties is required.
Optionally, existed status codes can be expended by additional header fields like X-Status-Reason: Validation failed
. Generally following status codes are enough:
200 OK |
OK, resourse is found, user has rights, requierd fields are correct |
404 Not Found |
Resourse is not found |
403 Forbidden |
User has not rights on resource access |
409 Conflict |
In case of repeated creating of resource (e.g. user registration with occupied nickname) |
422 Unprocessable Entity |
Form has incorrect filled fields |
500 Internal Server Error |
In case of unexpected exception on the server side |
Usefull discussion on the stackoverflow: REST HTTP status codes Proper use of HTTP status codes in a “validation” server
Manual page about routing in Ruby on Rails: Rails Routing from the Outside In
Roy Fielding's thesis about REST: Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation, University of California, Irvine, 2000.