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

    → calls JacksonJsonProvider

  • Accept: application/vnd-mycompany-error, application/json

    → calls MyBodyWriter

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.

+3


source to share


1 answer


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 :)

+2


source







All Articles