Search code examples
javajerseyjax-rscxf

Empty body when response has error status (Apache CXF)


Imagine there is a rest endpoint listening on host.tld/api and which returns a 404 Not Found with the following body:

{
    "status": 404,
    "message": "This is a custom error message",
    "errorNr": 13400
}

Additionally there is a ClientResponseFilter which looks like this:

public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
        if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) {
            // get the real error message
            CustomExceptionData error = new ObjectMapper().readValue(
                responseContext.getEntityStream(),
                CustomExceptionData .class
            );
            throw new CustomException(error.getErrorNr(), error.getStatus(), error.getMessage());
        }
}

The client uses this code to retrieve the response of the rest endpoint:

WebTarget target = getTarget();
try {
    return target.request(MediaType.APPLICATION_JSON_TYPE).get(MyCustomDTO.class);
} catch (Exception e) {
    if (e.getCause() instanceof CustomException) {
       // some other logic
    }
}

The code must work with the jersey and the apache cxf JAX-RS implementation. Now take a look at the last code block. When using jersey I get a javax.ws.rs.ProcessingException and executing e.getCause() returns a CustomException, so everything is correct. When using Apache CXF I get a javax.ws.rs.NotFoundException with absolutely no information about the response body and where e.getCause() returns null. Why is there such a difference? And how can I fix that?


Solution

  • Just before you throw your exception, you can set the status code to 200, which will prevent CXF from throwing a NotFoundException:

    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
            if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) {
                // get the real error message
                CustomExceptionData error = new ObjectMapper().readValue(
                    responseContext.getEntityStream(),
                    CustomExceptionData .class
                );
                responseContext.setStatus(Response.Status.OK.getStatusCode());
                throw new CustomException(error.getErrorNr(), error.getStatus(), error.getMessage());
            }
    }