Jersey - Content Negotiation for Error Handling
I would like to allow clients to choose the error response format using HTTP content negotiation.
eg. taking into account the end point
@Produces("application/json")
class MyService {
@GET
public String getSomething() {
if (currentTimeMilis() % 2 == 0) throw new MyException();
return "{ hello:\"world\" }";
}
and the block for displaying exceptions:
class MyExceptionMapper implements ExceptionMapper<MyException> {
@Override
public Response toResponse(MyException ex) {
return status(BAD_REQUEST)
.entity(new MyDto(randomUUID(), ex.getMessage()))
.build();
}
}
and with two authors, MyBodyWriter
and standard JacksonJsonProvider
.
I would like to call one of the authors depending on the content of the Accept header for example.
-
Accept: application/json
→ callsJacksonJsonProvider
-
Accept: application/vnd-mycompany-error, application/json
→ callsMyBodyWriter
I've tried different approaches, but they all fail because matching HttpRule
implies content type application/json
.
The only workaround I've found is to add request headers to ExceptionMapper
and explicitly specify the content type, but I don't like that.
source to share
Maybe a workaround, but ... you can try using ContainerResponseFilter
, get all generated MediaTypes from, ContainerRequestContext
and restrict the MediaType response to reset an object with ContainerResponseContext.setEntity
.
There may be better solutions!
Jersey 2.12 used:
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.List;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import path.to.MyException;
@Provider
public class EntityResponseFilter implements ContainerResponseFilter {
@Override
public void filter( ContainerRequestContext reqc , ContainerResponseContext resc ) throws IOException {
List<MediaType> mediaTypes = reqc.getAcceptableMediaTypes();
MediaType mediaType = new MediaType("application", "vnd-mycompany-error");
if( mediaTypes.contains( mediaType) && resc.getEntity() instanceof MyDao /* or MyDto, or null, or whatever */) {
resc.setEntity( resc.getEntity(), new Annotation[0], mediaType );
}
// ...
}
}
Hope this was helpful somehow :)
source to share