JAX-RS Exception Handling

Exception handling

Exceptions are in general very useful, they help you to understand what could go wrong or what was wrong.

When you are developing web services, if you propagate exceptions properly, they could help you to reduce the number of request from developers using your API asking help or support, by implementing meaningful responses, i.e., the proper implementation of HTTP status codes and providing as much information as possible in the response.

Customer facing exceptions

You must split your errors in two categories:

  • Client errors (400-series HTTP response code) - 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.
  • Server errors (500-series HTTP response code) - Tell the client the server failed to fulfill an apparently valid request. The client can continue and try again with the request without modification.

Whenever if possible, you should use exceptions classes already defined in javax.ws.rs package that are related to HTTP response codes.

In order to provide a better experience to the final user/developer, I suggest to create an ExceptionMapper to intercept WebApplicationException based exceptions to include the exception message in the response to the user request:

In the previous example, WebApplicationExceptionMapperProvider expect a pipe (|) as a separator in the exception message to split it in two kind of messages, the first part is a high level error message final user oriented, the second part is a more detailed error message developer oriented (upon each specific exception raised, if no pipe is provided, developerMessage isn’t included in the response).

Example:

If you need to define your custom exceptions that doesn’t extend from WebApplicationException, you should create also your own ExceptionMapper for that exception/s, so that you can provide the same level of detail in the response to the user.

Without WebApplicationExceptionMapperProvider (Apache CXF has an WebApplicationExceptionMapper already defined and used by default, but it doesn’t include the exception message in the response to the user/developer):

With WebApplicationExceptionMapperProvider (the example class described above):

Internal exceptions

By internal exceptions I mean all exceptions (in the server side obviously).

We must ensure that all exceptions are being “converted” to a customer facing exceptions, there are two ways (see more above):

  • Catch those exceptions at some point in the api implementation layer and convert them in a WebApplicationException.
  • Register a ExceptionMapper for that exception, so that you can provide the same level of detail in the response to the user.

You should have in mind that there could be some hidden RuntimeExceptions (unchecked) that you won’t see until they appears, specially when you’re using third part libraries, e.g.:

@POST
@Path("/login")
@Produces(MediaType.APPLICATION_JSON)
public Response postLogin(LoginInput login) {
  throw new DynamoDBMappingException("This is an example");
}

DynamoDBMappingException extends from RuntimeException (unchecked), if you invoke the method, you will receive:

You could define a GenericExceptionMapperProvider implements ExceptionMapper<Throwable> such as:

As you can see now, the error is masqueraded:

JAX-RS supports exception inheritance as well. When an exception is thrown, JAX-RS will first try to find an ExceptionMapper for that exception’s type. If it cannot find one, it will look for a mapper that can handle the exception’s superclass. It will continue this process until there are no more superclasses to match against (read more).

Here some examples about errors formats from some well-known companies:

Reference: