Error handling in REST API with Jersey

Error handling is one of the most procrastinated and least enjoyable parts when writing code… I mean, why should the application not always work as expected, when we’ve written it with so much passion and care, and, you know, the clients of the application always send the right requests, right?!? Unfortunately things do go wrong from time to time, and when it does we should be prepared to some extent at least… This is especially the case when writing REST APIs, because clients only get a black box with specification, having no clue what to do when the expected answer doesn’t come back, unless we do something about it…

Bottom line:  error handling is essential when designing REST APIs.

In this post I will present how I’ve designed and implemented the error handling of a REST API with the help Jersey framework. The code is based on the open source project presented in the Tutorial – REST API design and implementation in Java with Jersey and Spring

1. Design

There are usually two causes for error occurence: either something failed on the server or the client of the API sent an invalid request. In both cases I’ve decided to send back a response with some metadata that should be useful for client, and potentially the developers of the client application. But I also didn’t forget the developers of the API and the people monitoring it, because most likely they will be the ones that would have to react and debug when something goes wrong.

Note: The design presented here is based on the great work and resources I stumbled upon from the post – Resources on how to design error handling in a REST API.

The first way to let the client know that an error occurred, is by sending HTTP error codes in the response:

  • 4xx Client error –  codes are used to tell the client that a fault has taken place on THEIR side. They should not re-transmit the same request again, but fix the error first.
  • 5xx Server error – codes that tell the client the server failed to fulfill an apparently valid request.  The client can continue and try again with the request without modification.
  • The second thing is to give the API clients as much information as possible about the error – based on reason, necessity and best practices found in the resources mentioned above, I’ve come up with the following structure for error messages:

      
        {
           "status": 400,
           "code": 4000,
           "message": "Provided data not sufficient for insertion",
           "link": "http://www.codingpedia.org/ama/tutorial-rest-api-design-and-implementation-with-jersey-and-spring",
           "developerMessage": "Please verify that the feed is properly generated/set"
        }
      
    

    Here’s the explanation for each of the properties in the response:

  • status – holds redundantly the HTTP error status code, so that the developer can “see” it without having to analyze the response’s header
  • code – this is an internal code specific to the API (should be more relevant for business exceptions)

  • message – short description of the error, what might have cause it and possibly a “fixing” proposal
  • link – points to an online resource, where more details can be found about the error
  • developerMessage – detailed message, containing additional data that might be relevant to the developer. This should only be available when the “debug” mode is switched on and could potentially contain stack trace information or something similar
  • 2. Implementation

    Now that I know how errors should look like, let’s write some code to make this a reality. In Jersey you have currently to possibilities to handle exceptions via

    • WebApplicationExceptionOR/AND
    • mapping exceptions to responses via ExceptionMappers

    I like the second approach because it gives more control on the error entity I might send back. This is the approach I used throughout the tutorial.

    2.1. Checked(business) exceptions

    Checked Exceptions should be used to declare for expected, but unpreventable errors that are reasonable to recover from.“[3]

    First I packed the checked(business) exceptions under the AppException class. The structure of the class has exactly the properties mentioned for errors in the Design section, which are also mirrored in the ErrorMessage class holding the JSON entity in the response. With the help of the AppExceptionMapper:

      
        package org.codingpedia.demo.rest.errorhandling;
    
        import javax.ws.rs.core.MediaType;
        import javax.ws.rs.core.Response;
        import javax.ws.rs.ext.ExceptionMapper;
    
        public class AppExceptionMapper implements ExceptionMapper<AppException> {
    
        	public Response toResponse(AppException ex) {
        		return Response.status(ex.getStatus())
        				.entity(new ErrorMessage(ex))
        				.type(MediaType.APPLICATION_JSON).
        				build();
        	}
    
        }
      
    

    when an AppException occurs, let’s say for example you requested a podcast-resource that might have been deleted in the mean time:

      
        GET http://localhost:8888/demo-rest-jersey-spring/podcasts/22 HTTP/1.1
        Accept-Encoding: gzip,deflate
        Accept: application/json
        Host: localhost:8888
        Connection: Keep-Alive
        User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
      
    

    the exception mapper encapsulates the error message as JSON, with the HTTP status you desire and you might get a response like the following:

      
        HTTP/1.1 404 Not Found
        Content-Type: application/json
        Access-Control-Allow-Origin: *
        Access-Control-Allow-Methods: GET, POST, DELETE, PUT
        Access-Control-Allow-Headers: X-Requested-With, Content-Type, X-Codingpedia
        Content-Length: 231
        Server: Jetty(9.0.7.v20131107)
    
        {
           "status": 404,
           "code": 404,
           "message": "The podcast you requested with id 22 was not found in the database",
           "link": "http://www.codingpedia.org/ama/tutorial-rest-api-design-and-implementation-in-java-with-jersey-and-spring/",
           "developerMessage": "Verify the existence of the podcast with the id 22 in the database"
        }
      
    

    Note: After receiving this message, it should be clear to the client that the resource is not available anymore and can react to that accordingly.

    2.2. Unchecked(technical) exceptions

    “Unchecked Exceptions should be used for everything else.”[3]

    I’ve decided to catch all other exception that are not of type AppException, by implementing an ExceptionMapper on Throwable:

      
        package org.codingpedia.demo.rest.errorhandling;
    
        //imports omitted for brevity
    
        import org.codingpedia.demo.rest.filters.AppConstants;
    
        public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
    
        	@Override
        	public Response toResponse(Throwable ex) {
    
        		ErrorMessage errorMessage = new ErrorMessage();		
        		setHttpStatus(ex, errorMessage);
        		errorMessage.setCode(AppConstants.GENERIC_APP_ERROR_CODE);
        		errorMessage.setMessage(ex.getMessage());
        		StringWriter errorStackTrace = new StringWriter();
        		ex.printStackTrace(new PrintWriter(errorStackTrace));
        		errorMessage.setDeveloperMessage(errorStackTrace.toString());
        		errorMessage.setLink(AppConstants.BLOG_POST_URL);
    
        		return Response.status(errorMessage.getStatus())
        				.entity(errorMessage)
        				.type(MediaType.APPLICATION_JSON)
        				.build();
        	}
    
        	private void setHttpStatus(Throwable ex, ErrorMessage errorMessage) {
        		if(ex instanceof WebApplicationException ) {
        			errorMessage.setStatus(((WebApplicationException)ex).getResponse().getStatus());
        		} else {
        			errorMessage.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); //defaults to internal server error 500
        		}
        	}
        }
      
    

    Note:

  • this is not an invasive way of catching and presenting runtime exceptions; if one happens it will be presented to the client without cluttering the code…
  • if the error comes from Jersey (hence there is of type WebApplicationException), I inherit the HTTP status in from Jersey (line 28)
  • in the developerMessage property I set the error’s stack trace, thus giving the API developers a nice way to see, in browsers for example, what was the technical problem; the developerMessage should only be visible in debug-mode, or not at all if the security constraints impose it
  • in the online resource specified by the link property you could have a email/phone number listed where the clients of the API can give more details about the context of the error
  • 3. Testing

    Check out our video tutorial How to test a REST API with SoapUI, there are tests cases there covering error handling:

    You can find the complete test suite on GitHub.

    4. Custom Reason Phrase in HTTP status error message response with JAX-RS (Jersey)

    If for some reason you need to produce a custom Reason Phrase in the HTTP status response delivered when an error occurs than you might want to check the following post

    http://www.codingpedia.org/ama/custom-reason-phrase-in-http-status-error-message-response-with-jax-rs-jersey/

    5. Summary

    Well, that’s it. In this post you’ve learned one way to design error handling for a REST API and how you could implement it with Jersey.  I’d more than happy to hear about your approach on treating exceptions in REST APIs so if you have a suggestion please leave a message.

    6. Resources

    6.1. Source Code

    6.2. Codingpedia

    6.3. Web

    1. Checked and Unchecked Exceptions in Java
    2. When should we return 4xx or 5xx status codes to the client?
    3. Stackoverflow – When to choose checked and unchecked exceptions
    Podcastpedia image

    Adrian Matei

    Creator of Podcastpedia.org and Codingpedia.org, computer science engineer, husband, father, curious and passionate about science, computers, software, education, economics, social equity, philosophy - but these are just outside labels and not that important, deep inside we are all just consciousness, right?

    “Featured Image courtesy of Stuart Miles / FreeDigitalPhotos.net”

    How to get the title of a remote web page using javascript and NodeJS

    This post presents how to use web scraping with Cheerio in a NodeJS backend to retrieve the title of a bookmark added in bookmarks.codingpedia.org Continue reading

    How to use Showdown in Angular and NodeJS

    Published on February 21, 2017

    Sharing coding bookmarks

    Published on February 11, 2017