Generics with Spring RESTTemplate for a POST call - java

My question is similar to below question,
Generics with Spring RESTTemplate
The above solution is for a GET call, but am looking for a POST call and in my case input payload is also a wrapper object with generic.
This is how my controller function looks like:
ResponseWrapper<ResponseModel> apiForPost(
#RequestBody RequestWrapper<RequestModel> inputPayload,
#Context HttpHeaders headers) {
}
This is the sample code am trying and where am facing the issue
RequestWrapper<RequestModel> request = new RequestWrapper<>();
//code to set above request object
String url ="http://some_url";
RestTemplate restTemplate = new RestTemplate();
ResponseWrapper<ResponseModel> response = restTemplate.exchange(url,
HttpMethod.POST,
request,
new ParameterizedTypeReference<ResponseWrapper<ResponseModel>>() {}).getBody(); // shows error at that the 3rd parameter (request) cannot be of that type
As per the documentation this is what exchane method expects:
exchange(URI url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType)
(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html)
If anyone can help me on this, that would be great. Thanks.

Related

Error in getting image from Graph API in spring-boot with resttemplate

I am using the graph api:
GET /users/{id | userPrincipalName}/photo/$value
to get the particular user's profile photo with my access token. In postman I am able to see the image using the above get call.
In my spring-boot application I am using like below:
final ResponseEntity<Object> profilePicture = restTemplate.exchange(graphUrl, HttpMethod.GET, new HttpEntity<>((header)), new ParameterizedTypeReference<>() {});
I am getting below error:
Could not extract response: no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [image/jpeg]
I have defined RestTemplate like:
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
Could someone please help me with this?
You need to add an appropriate MessageConverter to your RestTemplate.
Something like:
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
ResponseEntity<byte[]> response = restTemplate.exchange(graphUrl,
HttpMethod.GET, new HttpEntity<>((header)), byte[].class);
You can read more on this subject here: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html

restTemplate.exchange(url,HttpMethod.POST,entity1,Response.class)) is returned null object instead of responseEntity stubbed data when mockito applied

This is my call to be mocked:
ResponseEntity<Response> response = restTemplate.exchange(url, HttpMethod.POST, entity, Response.class);
mocking like below :
#Test
public void getSpecalityInventoryItemsCallPositiveTest(){
RestTemplateBuilder restTemplateBuilder = Mockito.mock(RestTemplateBuilder.class);
RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
GetSpecalityInventoryItemsCaller getSpecalityInventoryItemsCaller = new GetSpecalityInventoryItemsCaller(restTemplateBuilder,id,pw);
Response inventoryItemsRespone = new Response();
inventoryItemsRespone.setStatusCode("0000");
BDDMockito.given(restTemplateBuilder.messageConverters(BDDMockito.any(ArrayList.class))).willReturn(restTemplateBuilder);
BDDMockito.given(restTemplateBuilder.additionalInterceptors(BDDMockito.any(ArrayList.class))).willReturn(restTemplateBuilder);
BDDMockito.given(restTemplateBuilder.build()).willReturn(restTemplate);
ResponseEntity<Response> responseEntity = new ResponseEntity<Response>(inventoryItemsRespone, HttpStatus.OK);
BDDMockito.given(restTemplate.exchange(url,HttpMethod.POST,HttpEntity.EMPTY,Response.class)).willReturn(responseEntity);
Response response = getSpecalityInventoryItemsCaller.getSpecalityInventoryItemsCaller(rxNumber);
Assertions.assertThat(response.getStatusCode()).isEqualTo("0000");
}
but getSpecalityInventoryItemsCaller.getSpecalityInventoryItemsCaller(rxNumber) is executed , inside restTemplate.exchange(url, HttpMethod.POST, entity, Response.class); call is returning null response
i need my own response Response
inventoryItemsRespone = new Response();
inventoryItemsRespone.setStatusCode("0000");
Not getting my own response , please help me out.
My guess is that the stubbing you are doing is matching on exact arguments. The problem is that the actual method may call may include arguments which have the same value as your stubbing but are not the same actual objects.
You should try using matchers for the restTemplate stubbing too:
BDDMockito.given(restTemplate.exchange(any(),any(),any(),any())).willReturn(responseEntity);
Although you might want to make it more precise than the any() matchers I have used here as an example, but it's worth testing to see if this works.

How to extract HTTP status code from the RestTemplate call to a URL?

I am using RestTemplate to make an HTTP call to our service which returns a simple JSON response. I don't need to parse that JSON at all. I just need to return whatever I am getting back from that service.
So I am mapping that to String.class and returning the actual JSON response as a string.
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
return response;
Now the question is -
I am trying to extract HTTP Status codes after hitting the URL. How can I extract HTTP Status code from the above code? Do I need to make any change into that in the way I doing it currently?
Update:-
This is what I have tried and I am able to get the response back and status code as well. But do I always need to set HttpHeaders and Entity object like below I am doing it?
RestTemplate restTemplate = new RestTemplate();
//and do I need this JSON media type for my use case?
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
//set my entity
HttpEntity<Object> entity = new HttpEntity<Object>(headers);
ResponseEntity<String> out = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
System.out.println(out.getBody());
System.out.println(out.getStatusCode());
Couple of question - Do I need to have MediaType.APPLICATION_JSON as I am just making a call to url which returns a response back, it can return either JSON or XML or simple string.
Use the RestTemplate#exchange(..) methods that return a ResponseEntity. This gives you access to the status line and headers (and the body obviously).
getStatusCode()
getHeaders()
If you don´t want to leave the nice abstraction around RestTemplate.get/postForObject... methods behind like me and dislike to fiddle around with the boilerplate stuff needed when using RestTemplate.exchange... (Request- and ResponseEntity, HttpHeaders, etc), there´s another option to gain access to the HttpStatus codes.
Just surround the usual RestTemplate.get/postForObject... with a try/catch for org.springframework.web.client.HttpClientErrorException and org.springframework.web.client.HttpServerErrorException, like in this example:
try {
return restTemplate.postForObject("http://your.url.here", "YourRequestObjectForPostBodyHere", YourResponse.class);
} catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) {
if(HttpStatus.NOT_FOUND.equals(httpClientOrServerExc.getStatusCode())) {
// your handling of "NOT FOUND" here
// e.g. throw new RuntimeException("Your Error Message here", httpClientOrServerExc);
}
else {
// your handling of other errors here
}
The org.springframework.web.client.HttpServerErrorException is added here for the errors with a 50x.
Now you´re able to simple react to all the StatusCodes you want - except the appropriate one, that matches your HTTP method - like GET and 200, which won´t be handled as exception, as it is the matching one. But this should be straight forward, if you´re implementing/consuming RESTful services :)
If you want all the HTTPStatus from a RestTemplate including 4XX and 5XX, you will have to provide an ResponseErrorHandler to the restTemplate, since the default handler will throw an exception in case of 4XX or 5XX
We could do something like that :
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
#Override
public boolean hasError(HttpStatus statusCode) {
return false;
}
});
ResponseEntity<YourResponse> responseEntity =
restTemplate.getForEntity("http://your.url.here", YourResponse.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.XXXX);
private RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(url,HttpMethod.GET, requestEntity,String.class);
response contains 'body', 'headers' and 'statusCode'
to get statusCode : response.getStatusCode();
exchange(...) works but if you want less code, you can use
org.springframework.boot.test.web.client.TestRestTemplate.getForEntity(...)
which returns an Entity containing StatusCode. Change your example code to this:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
HttpStatus statusCode = response.getStatusCode();
To test it you can use this snippet from my unit test:
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
assertResponseHeaderIsCorrect(response, HttpStatus.OK);
/**
* Test the basics of the response, non-null, status expected, etc...
*/
private void assertResponseHeaderIsCorrect(ResponseEntity<String> response, HttpStatus expectedStatus) {
assertThat(response).isNotNull();
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON_UTF8);
assertThat(response.getStatusCode()).isEqualTo(expectedStatus);
}
There can be some slightly trickier use cases someone might fall in (as I did). Consider the following:
Supporting a Page object in order to use it with RestTemplate and ParameterizedTypeReference:
RestPageResponse:
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
public class RestResponsePage<T> extends PageImpl<T>{
private static final long serialVersionUID = 3248189030448292002L;
public RestResponsePage(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public RestResponsePage(List<T> content) {
super(content);
}
public RestResponsePage() {
super(new ArrayList<T>());
}
}
Using ParameterizedTypeReference will yield the following:
ParameterizedTypeReference<RestResponsePage<MyObject>> responseType =
new ParameterizedTypeReference<RestResponsePage<MyObject>>() {};
HttpEntity<RestResponsePage<MyObject>> response = restTemplate.exchange(oauthUrl, HttpMethod.GET, entity, responseType);
Calling #exchange:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<?> entity = new HttpEntity<>(headers);
response = restTemplate.exchange("localhost:8080/example", HttpMethod.GET, entity, responseType);
Now here is the "tricky" part.
Trying to call exchange's getStatusCode will be impossible because the compiler, unfortunately, will be unaware of the "intended" type of response.
That is because generics are implemented via type erasure which removes all information regarding generic types during compilation (read more - source)
((ResponseEntity<RestResponsePage<MyObject>>) response).getStatusCode()
In this case, you have to explicitly cast the variable to the desired Class to get the statusCode (and/or other attributes)!
Putting this much of code is enough for me
HttpStatus statusCode = ((ResponseEntity<Object>) responseOfEsoft).getStatusCode();
You can use this solution
RestTemplate restTemplate = new RestTemplate();
final String baseUrl = "http://www.myexampleurl.com";
URI uri = new URI(baseUrl);
ResponseEntity<String> result = restTemplate.getForEntity(uri, String.class);
//get status code
int statuCode = result.getStatusCodeValue();
Was able to solve this through:
HttpEntity<Object> entity = restTemplate.getForEntity(uri, Object.class);
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
System.out.println(result.getStatusCode());

How do you get a converted request from RestTemplate after a GET/POST/PUT call?

I have no issues using Spring's RestTemplate to make POST or GET calls to an endpoint. I've also set the message converter as JSON (MappingJacksonHttpMessageConverter), no prob.
My question is, how can I grab the converted request I'm sending?
Example, if I do this:
ResponseEntity<T> result = this.restTemplate.postForEntity("http://{endpoint_url...}", dtoEntryObj, SomeDTO.class);
How could I grab the JSON that it's sending to the endpoint?
RestTemplate can send formats other than json as well. To get the actual body of the request, you will need to implement your own ClientHttpRequestInterceptor and use RestTemplate#setInterceptors() to register it.
Something like
RestTemplate template = new RestTemplate();
ClientHttpRequestInterceptor interceptor = new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
// you have access to the body
// do something with it
return execution.execute(request, body);
}
};
template.setInterceptors(Arrays.asList(interceptor));
Note that with json, RestTemplate uses a MappingJackson2MessageConverter which internally uses an ObjectMapper. You can simulate it if the above solution doesn't work for you.

How do I read the response header from RestTemplate?

I am posting information to a web service using RestTemplate.postForObject. Besides the result string I need the information in the response header. Is there any way to get this?
RestTemplate template = new RestTemplate();
String result = template.postForObject(url, request, String.class);
Ok, I finally figured it out. The exchange method is exactly what i need. It returns an HttpEntity which contains the full headers.
RestTemplate template = new RestTemplate();
HttpEntity<String> response = template.exchange(url, HttpMethod.POST, request, String.class);
String resultString = response.getBody();
HttpHeaders headers = response.getHeaders();
Best thing to do whould be to use the execute method and pass in a ResponseExtractor which will have access to the headers.
private static class StringFromHeadersExtractor implements ResponseExtractor<String> {
public String extractData(ClientHttpResponse response) throws
{
return doSomthingWithHeader(response.getHeaders());
}
}
Another option (less clean) is to extend RestTemplate and override the call to doExecute and add any special header handling logic there.
HttpEntity<?> entity = new HttpEntity<>( postObject, headers ); // for request
HttpEntity<String> response = template.exchange(url, HttpMethod.POST, entity, String.class);
String result= response.getBody();
HttpHeaders headers = response.getHeaders();
I don't know if this is the recommended method, but it looks like you could extract information from the response headers if you configure the template to use a custom HttpMessageConverter.

Categories

Resources