Error handling in REST API with Jersey

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repos on Github âđ
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“
Contents
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:
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": "https://www.codepedia.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:
code â this is an internal code specific to the API (should be more relevant for business exceptions)
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": "https://www.codepedia.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:
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
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
- GitHub â Codingpedia/demo-rest-jersey-spring (instructions on how to install and run the project)
6.2. Codingpedia
- Tutorial â REST API design and implementation in Java with Jersey and Spring
- Compilation â Resources on how to design error handling in a REST API
- Video â How to test a REST API with SoapUI
6.3. Web
- Checked and Unchecked Exceptions in Java
- When should we return 4xx or 5xx status codes to the client?
- Stackoverflow â When to choose checked and unchecked exceptions
âFeatured Image courtesy of Stuart Miles / FreeDigitalPhotos.netâ