Reputation: 2027
Suppose I've got an endpoint in Dropwizard, say
@GET
public Response foo() { throw new NullPointerException(); }
When I hit this endpoint it logs the exception and everything, which is great! I love it. What I love less is that it returns a big status object to the user with status: ERROR
(which is fine) as well as a gigantic stack trace, which I'm less excited about.
Obviously it's best to catch and deal with exceptions on my own, but from time to time they're going to slip through. Writing a try catch block around the entire resource every time is fine, but (a) it's cumbersome, and (b) I always prefer automated solutions to "you have to remember" solutions.
So what I would like is something that does the following:
I feel like there must be a built-in way to do this -- it already handles exceptions in a relatively nice way -- but searching the docs hasn't turned up anything. Is there a good solution for this?
Upvotes: 8
Views: 7536
Reputation: 513
It worked for me by simply registering the custom exception mapper created in the run method of the main class.
environment.jersey().register(new CustomExceptionMapper());
where CustomExceptionMapper can implement ExceptionMapper class like this
public class CustomExceptionMapperimplements ExceptionMapper<Exception>
Upvotes: 0
Reputation: 2729
This article details Checked and Unchecked Exceptions implementation for Jersey with customized ExceptionMapper:
https://www.codepedia.org/ama/error-handling-in-rest-api-with-jersey/
Official Dropwizard documentation also covers a simpler approach, just catching using WebApplicationException:
@GET
@Path("/{collection}")
public Saying reduceCols(@PathParam("collection") String collection) {
if (!collectionMap.containsKey(collection)) {
final String msg = String.format("Collection %s does not exist", collection);
throw new WebApplicationException(msg, Status.NOT_FOUND)
}
// ...
}
https://www.dropwizard.io/en/stable/manual/core.html#responses
Upvotes: 0
Reputation: 386
Add the following to your yaml file. Note that it will remove all the default exception mappers that dropwizard adds.
server:
registerDefaultExceptionMappers: false
Write a custom exception mapper as below:
public class CustomExceptionMapper implements ExceptionMapper<RuntimeException> {
@Override
public Response toResponse(RuntimeException runtime) {
// ...
}
}
Then register the exception mapper in jersey:
environment.jersey().register(new CustomExceptionMapper());
Upvotes: 4
Reputation: 2027
As alluded to by reek in the comments, the answer is an ExceptionMapper
. You'll need a class like this:
@Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
@Override
public Response toResponse(RuntimeException runtime) {
// ...
}
}
You can do whatever logging or etc. you like in the toResponse
method, and the return value is what is actually sent up to the requester. This way you have complete control, and should set up sane defaults -- remember this is for errors that slip through, not for errors you actually expect to see! This is also a good time to set up different behaviors depending on what kind of exceptions you're getting.
To actually make this do anything, simply insert the following line (or similar) in the run
method of your main dropwizard application:
environment.jersey().register(new RuntimeExceptionMapper());
where environment
is the Environment
parameter to the Application's run
method. Now when you have an uncaught RuntimeException
somewhere, this will trigger, rather than whatever dropwizard was doing before.
NB: this is still not an excuse not to catch and deal with your exceptions carefully!
Upvotes: 5
Reputation: 31968
Already mentioned this under the comments, but then thought I would give it a try with a use case.
Would suggest you to start differentiating the Exception
that you would be throwing. Use custom exception for the failures you know and throw those with pretty logging. At the same RuntimeException
should actually be fixed. Anyhow if you don't want to display stack trace to the end user you can probably catch a generic exception, log the details and customize the Response and entity accordingly.
You can define a
public class ErrorResponse {
private int code;
private String message;
public ErrorResponse() {
}
public ErrorResponse(int code, String message) {
this.code = code;
this.message = message;
}
... setters and getters
}
and then within you resource code you can modify the method as -
@GET
public Response foo() {
try {
...
return Response.status(HttpStatus.SC_OK).entity(response).build();
} catch (CustomBadRequestException ce) {
log.error(ce.printStackTrace());
return Response.status(HttpStatus.SC_BAD_REQUEST).entity(new ErrorResponse(HttpStatus.SC_BAD_REQUEST, ce.getMessage())).build();
} catch (Exception e) {
log.error(e.printStackTrace(e));
return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage())).build();
}
}
Upvotes: 0