简体   繁体   中英

Jersey: Reading Content-Type:plain/text response as XML

I'm receiving a HTTP response from an external server that contains XML in the body.

However, the response header says Content-Type:plain/text

This is incorrect and should be application/xml . But, as I said, it's an external server which we cannot change.

The following code gives an error:

ClientResponse response = Client.create().resource(url).get(ClientResponse.class);
return response.getEntity(XmlResponse.class);

Exception:

com.sun.jersey.api.client.ClientHandlerException: A message body reader for Java class com.evs.ats.XmlResponse, and Java type class com.evs.ats.XmlResponse, and MIME media type text/plain was not found

The following code works but I don't like it:

String resultString = response.getEntity(String.class);
InputStream stream = new ByteArrayInputStream(
    resultString.getBytes(StandardCharsets.UTF_8)
);
JAXBContext jc = JAXBContext.newInstance(XmlResponse.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
return (XmlResponse) unmarshaller.unmarshal(stream);

Is there any way to "force" Jersey to read this as XML anyway? Or is my only option to unmarshal it manually using JAXB (like above)? Or is there another option?

If the upstream server you're connecting to returns wrong value in the Content-Type HTTP header field, and it cannot be "reasoned with" (to fix the value returned), there is no other way (AFAIK) apart from providing your own custom implementation of MessageBodyReader .

One way of doing it with Jersey 2 would be:

public class TextAsXmlMessageBodyReader implements MessageBodyReader<Object> {

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
            Annotation[] annotations, MediaType mediaType) {
        return mediaType.getType().equals("text")
                && mediaType.getSubtype().equals("plain");
    }

    @Override
    public Object readFrom(Class<Object> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders,
            InputStream entityStream) throws IOException, WebApplicationException {
        try {
            final JAXBContext jc = JAXBContext.newInstance(type);
            final Unmarshaller unmarshaller = jc.createUnmarshaller();

            return unmarshaller.unmarshal(entityStream);
        } catch (JAXBException e) {
            throw new WebApplicationException("Error while unmarshalling the response.", e);
        }
    }
}

Then simply register this class with Jersey's client you are using. If you're using the same instance of the client in your whole application, you can register the custom message body reader per target (so it will not be used for responses from other "sane" servers, if you happen to have other upstream servers you're communicating with). Example of registering with target:

return ClientBuilder.newClient()  // Get a client; does not have to be a *new* client
        .target(url)              // Target the client to the particular upstream URL
        .register(TextAsXmlMessageBodyReader.class)  // Register the reader as described
        .request()                // Start building a request
        .get(XmlResponse.class);  // Issue a GET request (can be any HTTP method actually)

The advantage of having custom reader implementation is that it will be possible to write nice streamlined code which focuses on configuring and making the request, obtaining the response, and processing it if needed. Also, the reader implementation can be reused if you need to make requests against different resources on the target server (and all of them specify wrong Content-Type ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM