2

Short version of the question:

Using Jersey, how can I determine the @Produces type at runtime?

Long version of the question:

I wrote a REST call using jersy as follows:

@GET
@Path("/getVideo")
@Consumes(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
public Response getAllVideos(@QueryParam("videoID") Long videoID) throws ApiException, SQLException {
    --some code--
    Response r ...;
    return r;
}

If the user provides a valid videoID then this should return an mp4 file, hence the @Produces({MediaType.APPLICATION_OCTET_STREAM,. However, if an exception is thrown, such as a providing wrong videoID I want to return a json describing the exception.

The way it currently works is, that if a valid ID is provided, it returns a 200 with the mp4 file. But if an exception is thrown, it responds with a 500 and a message Could not find MessageBodyWriter for response object of type: com.my.package.Errors$CustomError of media type: application/octet-stream.

Based on the Jersey documentation the response's return type is determined is by the accept type of the request.

My problem is that I don't know in advance, when sending the request what type of response I want back (because I hope the request will be successful). Instead, I want to determine the response type at runtime based on whether or not an exception was thrown.

How can I do that?

(I think my question is similar to this question but I am not using Spring).

RealUser
  • 75
  • 8

2 Answers2

2

This will probably work because your exception says it couldn't find a message writer for CustomError

@Provider
@Produces(MediaType.APPLICATION_OCTET_STREAM) //I think you will have to leave this as octet_stream so jax-rs will pick as valid message writer
public class CustomErrorBodyWriter implements MessageBodyWriter<CustomError> {

    @Override
    public boolean isWriteable(Class<?> type, Type genericType,
                               Annotation[] annotations, MediaType mediaType) {
        return type == CustomError.class;
    }

    @Override
    public long getSize(CustomError customError, Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        // deprecated by JAX-RS 2.0 and ignored by Jersey runtime
        return 0;
    }

    @Override
    public void writeTo(CustomError customError, Class<?> type, Type genericType, Annotation[] annotations,
                        MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
                        OutputStream out) throws IOException, WebApplicationException {

        //manipulate the httpHeaders  to have content-type application/json

        //transalate customError and write it to OutputStream          

        Writer writer = new PrintWriter(out);
        writer.write("{ \"key\" : \"some random json to see if it works\" }");


        writer.flush();
        writer.close();
    }
}
  • 1
    Thanks, this worked for me. Even though I have the "@provider" tag, I still had to add this class explicitly to the resource config /"Application" class but then it worked like you said. And all I needed to add was: ArrayList content = new ArrayList<>(); content.add("application/json"); httpHeaders.replace("Content-Type", Collections.singletonList(content)); And then convert the "custom error" into a json string/ – RealUser Jun 23 '20 at 20:27
0

What you need is to provide to the client that accepts both types. Here is described this solution.

Radu Linu
  • 1,143
  • 13
  • 29