Writing a Dropwizard JSON AppPublished on
This article assumes some precursory knowledge of Dropwizard, a framework that has been invaluable when creating JSON oriented RESTful web services. Dropwizard is incredibly fast (I should insert a citation here, but metrics provided with the framework suggest there is neglible overhead due to itself). But maybe the most important aspect of the framework is that there is very little boilerplate to it (and we’re talking about Java here). It’s not as terse as some python microframeworks (I’m looking at you Flask and bottle.py), but I’ll compromise.
The one thing that Dropwizard doesn’t assume and that we need to add is making all responses in JSON. It is straightforward to make endpoints return JSON. The tricky part are exceptions. Mapping exceptions to the appropriate JSON response is what we’ll accomplish here. As a side note, Dropwizard isn’t alone in not implementing JSON exceptions by default. No framework that I know of does this (meteor might, but I’ve never used it, and meteor is a platform not a framework).
For the sake of convenience, I will inline Dropwizard’s Error Handling section and then I’ll go on to break it down.
If your resource class unintentionally throws an exception, Dropwizard will log that exception (including stack traces) and return a terse, safe text/plain 500 Internal Server Error response.
If your resource class needs to return an error to the client (e.g., the requested record doesn’t exist), you have two options: throw a subclass of Exception or restructure your method to return a Response.
If at all possible, prefer throwing Exception instances to returning Response objects.
If you throw a subclass of WebApplicationException jersey will map that to a defined response.
If you want more control, you can also declare JerseyProviders in your Environment to map Exceptions to certain responses by calling JerseyEnvironment#register(Object) with an implementation of javax.ws.rs.ext.ExceptionMapper. e.g. Your resource throws an InvalidArgumentException, but the response would be 400, bad request.
- Dropwizard defaults to text reply (we want JSON)
- Prefer throwing exceptions (ok, this is good, we don’t have to change APIs to add error handling)
- We must define an
ExceptionMapperto transform these exceptions into a JSON response
The correct solution to map expected exceptions into JSON is to define a class that implements
ExceptionMapper<WebApplicationException>. This specific implementation is important because when the server detects an HTTP 404 Not Found, 405 Method Not Allowed, 415 Unsupported Media Type, etc, the server throws an instance of
WebApplicationException. We want to catch these exceptions and turn them into JSON, otherwise Dropwizard would turn them into a plaintext respones.
The following is a class that has worked well in production.
To make this class effective in your application, add it to the application Jersey’s environment.
I alluded to it earlier, our curent solution works well for exceptions that we know exist in our program. In many server side programs there are exceptions that are infeasible to handle and so the only solution is to let the client know that something is wrong – our only requirement is that we want to let the user know with a JSON response and an HTTP 500 Internal Server Error. A great example of an unexpected error is if the database disconnected, a timeout ocurred, or maybe a rookie mistake was made and a null pointer was dereferenced
The solution to these problems is to add an
RuntimeException. Since these are unexpected exceptions, I often like verbose error messages that allow me to track down the problem and see if I can fix it. This involves capturing the stacktrace. For brevity, I won’t post an example, but it is safe to say it is similar to the example prior, except that a HTTP 500 status is always returned.
If you use authentication within Dropwizard, the default response when the user fails to authenticate is an plain text response. Obviously, this does not fit in with our JSON app, so we override by defining our own custom
UnauthorizedHandler and register it our
AuthFactory. Here’s a simple implementation:
As an added bonus, it can be immensly helpful to add metrics to the various errors that can occur to provide a deeper insight. I highly recommed it just make sure these metrics are more meaningful than the ones that currently ship with Dropwizard. There are metrics on 4xx and 5xx out of the box.
A good eye would notice that an
RuntimeException could handle
RuntimeException as one of its ancestors. Jersey is able to determine what
ExceptionMapper to use by examining the exception being thrown and what each mapper handles. It finds the closest implementation by looking at the inheritance hierarchy. Since
WebApplicationException derives directly from
RuntimeException, the distance is one. Jersey finds the
ExceptionMapper with the minimum distance, hence why we can have both mappers and they don’t conflict with each other. If you’d like to take a look at how Jersey implements this, the source code is on Github.