How to use RestTemplate with multiple response types?
Asked Answered
H

7

14

I'm using spring RestTemplate for communication with a xml webservice backend as follows:

ResponseEntity<MainDTO> dto = restTemplate.postForObject(url, postData, MainDTO.class);

Problem: the backend might either respond with MainDTO for normal data or with ErrorDTO in case of failures. But both with HTTP 200.

But I don't know which object will come back before! Anyways restTemplate requires me to pass the class type before.

So, how could I parse the xml either to normal or the error bean?

Sidenote: I don't have any control of the webservice backend.

Handmade answered 4/3, 2016 at 13:50 Comment(0)
L
8

As you would figure, the problem is that the backend should return you errors with HTTP error codes, that's what they are there for.

But as you said, you don't have control over the backend so what you can do is first get it as a String

ResponseEntity<String> dto = restTemplate.postForObject(url, postData, String.class);

Then you can attempt to parse the string response as a MainDTO with either Jackson or Gson (whatever you have in your project, which you should, because I believe Spring's RestTemplate uses either on of them internally) with a try/catch and if it fails, then you try with to parse it with your ErrorDto.

Update

Oh, I just read that it was an XML service, not a JSON on, the approach above is still valid, but instead of using Jackson or Gson, you can use SimpleXML (http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php#deserialize) which allows you to deserialize XML in an "easy" way, you just need to annotate your models with their annotations which are described in their tutorials and examples.

This Spring's example (http://spring.io/guides/gs/consuming-rest-xml-android/) might also provide an insight in how to use SimpleXML.

Lingonberry answered 4/3, 2016 at 14:13 Comment(0)
H
3

You could implement a custom ResponseErrorHandler that converts an erroneous response into a RuntimeException. Http Message to POJO conversion could be done by re-using the messageConverter(s) the RestTemplate#setMessageConverters is using.

Here is an example that utilizes this.

Hunkydory answered 4/3, 2016 at 16:1 Comment(0)
H
2

In my case this does not work, as the ResponseBody is a Map generated by OpenApi Feign Code. But its possible to map the Map to the corresponding object with an object mapper:

ResponseEntity<Object> registerResult = restTestRestTemplate.postForEntity("/register", registerRequest, Object.class);
Assertions.assertEquals(HttpStatus.NOT_ACCEPTABLE, registerResult.getStatusCode());
ObjectMapper objectMapper = new ObjectMapper();
InlineResponse406 inlineResponse406 = objectMapper.convertValue(registerResult.getBody(), InlineResponse406.class);
Assertions.assertNotNull(inlineResponse406);

        

this is code of a test class, of course, corresponding to other response codes other Objects can be created.

Hill answered 31/8, 2023 at 16:32 Comment(0)
P
0

I have the same problem so I have an abstract class that describes the error. All of my Json classes then extended the abstract error class. So the response object is populated with entity data and an error that I could check easily.

I don't particularly like this solution but as I immediately transform the Json object into an application data object it doesn't feel too bad.

Pyramidal answered 1/8, 2017 at 13:17 Comment(0)
H
0

I tried Alfredo's solution but I got an Exception because of Object.class:

Method threw 'org.springframework.web.client.HttpClientErrorException' exception.

If you use a different class, it would be casted and you wouldn't be able to use instance of.

Using following code solved my problem:

String xml = restTemplate.getForObject(url, String.class, uriVariables);
JAXBContext jaxbContext = JAXBContext.newInstance();
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringSource xmlStr = new StringSource(xml);
Object o = jaxbUnmarshaller.unmarshal((Source) xmlStr);
if (o instanceof VonAZRAZRBestaetigungMeldung090098) {
    // ...
}
Hispaniola answered 7/6, 2019 at 13:30 Comment(0)
W
0

I had the same problem, I solved it like this:

I created a Response class and inside I put the two response objects. I have noted all three classes with

@XmlRootElement
@XmlAccessorType (XmlAccessType.FIELD)

ResponseEntity <Response> responseEntity = restTemplate.postForEntity (url, request, Response.class);
Object res = responseEntity.getBody ();

if (res! = null) {
    if (res instanceof MainDTO) {
        ...
    } else if (res instanceof ErrorDTO) {
        ...
    }
}

Hope it's useful!

Wounded answered 2/12, 2020 at 12:3 Comment(1)
instanceof is an anti pattern not recommended in JAVA.Mccrea
V
-3

Using Instanceof

MyObject1 a=null;
MyObject2 b=null;
ResponseEntity<Object>response=template.exchange(builder.build().encode().toUri(),HttpMethod.GET,entity, Object.class);
if (response.getBody() instanceof MyObject1)
    a= (MyObject1) response.getBody();
else if(response.getBody() instanceof MyObject2)
    b= (MyObject2) response.getBody();
Virtuosic answered 22/2, 2018 at 17:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.