Spring - generic method for getting body from ResponseEntity using HttpEntity - java

In my code I am very often using HttpEntity alongside ResponseEntity in a following way:
HttpEntity<?> request = new HttpEntity<String>(myObject, headers);
ResponseEntity<String> response = restTemplate.exchange("someurl", HttpMethod.POST, request, String.class);
and then
response.getBody()
I repeat this code all the time, I was wondering if it is possible to create a generic method that would allow me to get response.body() when I supply it with object I want to send, an url, and a HttpMethod type.
The response body most of the time will be a string, but can be an object.

You can use below code, here the response body and request body are made generic:
public <T, R> T yourMethodName(R requestBody,
MultiValueMap<String, String> headers,
String url,
HttpMethod type,
Class<T> clazz) {
HttpEntity<?> request = new HttpEntity<String>(requestBody, headers);
//You have to create restemplate Obj somewhere
ResponseEntity<T> response = restTemplate.exchange(url, type, request, clazz);
return response.getBody();
}

Related

Mock RestTemplate.exchange using Mockito

Here is my code that I want to mock.
Map<String, String> params = new HashMap<>();
params.put(LOGIN_ID, loginId);
params.put(LOGIN_PWD, loginPwd);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity httpEntity = new HttpEntity<>(params, headers);
ResponseEntity<HashMap> omsResponse = restTemplate.exchange(loginUrl, HttpMethod.POST, httpEntity, HashMap.class);
Here is the test I am running.
PromotionDto promotionDto = getPromotionDto();
ResponseEntity<HashMap> omsResponse = new ResponseEntity<>(new HashMap(), HttpStatus.OK);
Map<String, String> params = new HashMap<>();
HttpHeaders headers = new HttpHeaders();
HttpEntity httpEntity = new HttpEntity<>(params, headers);
String loginUrl = "https://dev.example.com/smcfs/restapi/invoke/login";
Mockito.when(restTemplate.exchange(loginUrl, HttpMethod.POST, httpEntity, HashMap.class)).thenReturn(omsResponse);
OrderCaptureOMSResponse response = omsService.orderCapture(promotionDto, IS_EMPLOYEE);
The error I am getting
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
this invocation of 'exchange' method:
restTemplate.exchange(
null,
POST,
<{LoginID=null, Password=null},[Content-Type:"application/json"]>,
class java.util.HashMap
);
-> at com.qurateretail.order.promotion.service.OmsServiceImpl.omsInterfaceLogin(OmsServiceImpl.java:87)
has following stubbing(s) with different arguments:
restTemplate.exchange(
"https://dev.example.com/smcfs/restapi/invoke/login",
POST,
<{},[]>,
class java.util.HashMap
);
-> at com.qurateretail.order.promotion.service.OmsServiceTest.orderCaptureTest(OmsServiceTest.java:40)
Typically, stubbing argument mismatch indicates user mistake when writi
When copying your code and running it myself it worked fine when using correctly ;).
ResponseEntity<HashMap> omsResponse = new ResponseEntity<>(new HashMap(),
HttpStatus.OK);
Map<String, String> params = new HashMap<>();
HttpHeaders headers = new HttpHeaders();
HttpEntity httpEntity = new HttpEntity<>(params, headers);
String loginUrl = "https://dev.example.com/smcfs/restapi/invoke/login";
Mockito.when(restTemplate.exchange(loginUrl, HttpMethod.POST, httpEntity, HashMap.class)).thenReturn(omsResponse);
ResponseEntity<HashMap> exchange = restTemplate.exchange(loginUrl, HttpMethod.POST, httpEntity, HashMap.class);
System.out.println(exchange);
From the error Message
his invocation of 'exchange' method: restTemplate.exchange( null, POST,
<{LoginID=null, Password=null},[Content-Type:"application/json"]>, class
java.util.HashMap );
you can see that in your Code the parameter "loginUrl" is different then what you are mocking. In your Mock it mocks "https://dev.example.com/smcfs/restapi/invoke/login" as loginUrl. In your actual execution the code has null for the URL. The Mock only works if the Mock and the call of that method has the exact same parameters. Why your loginUrl is null is something i can not say with the Code you provided. The relevant parts for that are missing ^^.
Another approach would be to generalize the mock.
Mockito.when(restTemplate.exchange(any(), ...
this is however a bit tricky with restTemplate since the method Overloading of "exchange" make it difficult. Matching the arguments is better anyways.

How to restremplate to deletemapping with requestbody of list of objects

I am trying to connect an endpoint which is in other profile and it defined as following:
#DeleteMapping("/persons")
public ResponseEntity<Boolean> deletePersons(#RequestBody List<Persons> persons) {
I send a resttemplate as following but it does not work:
public ResponseEntity<Boolean> deletePersons(List<Persons> persons) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_OCTET_STREAM));
HttpEntity<?> httpEntity = new HttpEntity<>("persons", headers);
return restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, Boolean.class,
persons);
}
i am getting the following error:
No HttpMessageConverter for java.lang.String
I think here you are getting back a String as response but you want to return a Boolean with your function, and it cant't convert the String to Boolean.
return restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, Boolean.class,
persons);

How to log spring webclient request and response body?

added an ExchangeFilterFunction to WebClient which logs request and response but while logging, was unable to log request and response body as a string or JSON. It prints as an object
Tried different castings and retrieving the body as a string using bodyToMono, toEntity but they return an object and not string exactly.
logRequest(clientRequest.url(), clientRequest.method(), clientRequest.headers(), clientRequest.body());
Mono<ClientResponse> response = exchangeFunction.exchange(clientRequest);
ClientResponse clientResponse = response.block();
logResponse(clientResponse.statusCode(), clientResponse.headers(), clientResponse.toString(), clientResponse);
return response;
}```
```private void logRequest(URI uri, HttpMethod method, HttpHeaders httpHeaders, BodyInserter<?, ? super ClientHttpRequest> body) {
log.info("Request: {}",
LogVar.with("Body", body)
);
}
private void logResponse(HttpStatus statusCode, Headers headers, String body, ClientResponse extractor) {
log.info("Response: {}",
, LogVar.with("Body", extractor.bodyToMono(String.class))
);
}```
Expecting Json OR XML whatever the request/response is, to be logged as a string.

How to get HttpRequest getEntity data in Spring MVC controller?

private ArrayList<NameValuePair> mParams;
HttpClient client = new DefaultHttpClient();
mParams = new ArrayList<NameValuePair>();
mParams.add(new BasicNameValuePair("testKey", "John"));
mParams.add(new BasicNameValuePair("testSerial", "003-100"));
HttpPost request = new HttpPost("http://localhost:8080/test/getRequiredEnv");
request.setEntity(new UrlEncodedFormEntity(mParams, HTTP.UTF_8));
HttpResponse response = client.execute(request);
// TestController.java
#RestController
public class TestController {
private static final Logger logger = Logger.getLogger(TestController.class);
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
#RequestParam("testKey") String testKey,
#RequestParam("testValue") String testValue,
#RequestHeader HttpHeaders headers) {
logger.info("Test Key [" + testKey + "]");
logger.info("Test Value [" + testValue + "]");
return new TestResponseInfo("0001", "ABC");
}
Can someone please tell me is this the correct way to get data from 'Request.setEntity' in SpringMVC rest controller or I am missing something?
Secondly, in postman "httpPost" request I pass the parameters (testKey & testValue) as headers or as body?
Thirdly, without knowing the parameters in httpPost request can I able to parse the incoming request and extract the parameters from it in Spring controller?
First of all it would be good to know the content-type of the request that is sent.
So I guess you want to get the body of the request. To get all request parameters if you don't know the parameter names beforehand you can use #RequestParam with type Map<String, String> to get all params:
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
#RequestParam Map<String, String> allParams,
#RequestHeader HttpHeaders headers)
But I am not sure if this works because it also depends on the content-type. E.g. for form data (application/x-www-form-urlencoded) have a look at the Spring documentation about #RequestBody which states about one of the default message converters FormHttpMessageConverter:
FormHttpMessageConverter converts form data to/from a MultiValueMap.
So try:
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
#RequestBody MultiValueMap<String, String>,
#RequestHeader HttpHeaders headers)
Alternatively there is also HttpServletRequest.getParameterMap() which gets you a Map. You can get the request by just including HttpServletRequest request as a method argument.
If you know the paramters beforehand, annotating your POJO that resembles the form data with #ModelAttribute should also work like so:
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
##ModelAttribute Test myTestPojo,
#RequestHeader HttpHeaders headers)
Or you could also send data as application/json and when including jackson as a dependency, #Requestbody will map your data to a POJO. Have a look at e.g. Spring JSON request body not mapped to Java POJO.
In regard to your second question httpPost will pass the parameters as body since it is a POST request.

What is the difference between ResponseEntity<T> and #ResponseBody?

I have a simple handler in my controller which returns a message
#RequestMapping(value = "/message")
#ResponseBody
public Message get() {
return new Message(penguinCounter.incrementAndGet() + " penguin!");
}
At the same time I can use something like this
#RequestMapping(value = "/message")
ResponseEntity<Message> get() {
Message message = new Message(penguinCounter.incrementAndGet() + " penguin!");
return new ResponseEntity<Message>(message, HttpStatus.OK);
}
What is the difference betweet this two approaches? Let's not take into account HttpStatus :)
ResponseEntity will give you some added flexibility in defining arbitrary HTTP response headers. See the 4th constructor here:
http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/http/ResponseEntity.html
ResponseEntity(T body, MultiValueMap<String,String> headers, HttpStatus statusCode)
A List of possible HTTP response headers is available here:
http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Responses
Some commonly-used ones are Status, Content-Type and Cache-Control.
If you don't need that, using #ResponseBody will be a tiny bit more concise.
HttpEntity represents an HTTP request or response consists of headers and body.
// Only talks about body & headers, but doesn't talk about status code
public HttpEntity(T body, MultiValueMap<String,String> headers)
ResponseEntity extends HttpEntity but also adds a Http status code.
// i.e ResponseEntity = HttpEntity + StatusCode
public ResponseEntity(T body, MultiValueMap<String,String> headers, HttpStatus statusCode)
Hence used to fully configure the HTTP response.
For Ex:
#ControllerAdvice
public class JavaWebExeptionHandler {
#Autowired
ExceptionErrorCodeMap exceptionErrorCodeMap;
#ExceptionHandler(RuntimeException.class)
public final ResponseEntity<ExceptionResponseBody> handleAllExceptions(Exception ex) {
Integer expCode = exceptionErrorCodeMap.getExpCode(ex.getClass());
// We have not added headers to response here, If you want you can add by using respective constructor
return new ResponseEntity<ExceptionResponseBody>(new ExceptionResponseBody(expCode, ex.getMessage()),
HttpStatus.valueOf(expCode));
}
}
#ResponseBody indicates that return value of method on which it is used is bound to the response body
(Mean the return value of method is treated as Http response body)
ResponseEntity<> is a generic class with a type parameter, you can specify what type of object to be serialized into the response body.
#ResponseBody is an annotation, indicates that the return value of a method will be serialized into the body of the HTTP response.
you can set headers using ResponseEntity<>
#ResponseEntity represents a response which includes headers, body and status code.
#ResponseBody only returns the body of the response.

Categories