Денис Крешихин

Денис
Крешихин

software development, computer science

2024 © Денис Крешихин

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:

  1. Collecting of data fields from a form and creating a object which incapsulated this data.
  2. The data are sending through the post/put request to an server.
  3. 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:

  1. The error object may to care additional information.
  2. Count of states is not limited.
  3. Knowing of HTTP niceties is not required.

Cons:

  1. Response semantic is broken. Invalid request may returns HTTP status code '200 OK'.
  2. 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:

  1. Semantics of HTTP and application status codes coincide like models coincide with resource names and methods of controllers with types of HTTP requests.
  2. Duplication of application and software levels is excluded.
  3. A specification of error statuses is not required.

Cons:

  1. Not always needed status code exist.
  2. 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.