3

I'd like to allow clients to select an error response format using HTTP content negotiation.

E.g. given an endpoint

@Produces("application/json")
class MyService {

 @GET
 public String getSomething() {
    if (currentTimeMilis() % 2 == 0) throw new MyException();

    return "{ hello:\"world\" }";
 }

and exception mapper:

class MyExceptionMapper implements ExceptionMapper<MyException> {

    @Override
    public Response toResponse(MyException ex) {

        return status(BAD_REQUEST)
                .entity(new MyDto(randomUUID(), ex.getMessage()))
                .build();
    }

} 

and having two body writers, MyBodyWriter and standard JacksonJsonProvider.

I'd like to invoke one of the writers depending on the contents of Accept header, e.g.

  • Accept: application/json -> invokes JacksonJsonProvider
  • Accept: application/vnd-mycompany-error, application/json -> invokes MyBodyWriter

I had tried different approaches but all of them fail because the matched HttpRule implies an application/json content type.

The only workaround I've found is to inject the request headers into ExceptionMapper and set the content type explicitly there -- but I don't like it.

4

1 回答 1

2

Maybe just a workaround, but ...
You could try to use a ContainerResponseFilter, get all accpeted MediaTypes from the ContainerRequestContext and limit the response MediaType by reset the entity with ContainerResponseContext.setEntity.

There might be better solutions!
Used jersey 2.12:

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

于 2014-09-13T08:35:35.550 回答