Rest client with Spring RestTemplate and custom Object input parameter - spring

This is my rest controller (server):
#RestController
public class RemoteController {
#RequestMapping(value="/test", method=RequestMethod.GET)
public Return serverTest(HttpServletRequest req, SearchFilter search) throws Exception{
//...
return new OutputTest();
}
}
I want to write the corresponding client for this GET controller with SearchFilter object as input.
public void clientTest(){
SearchFilter input=new SearchFilter();
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = input;// how to store SearchFilter input ??????
ResponseEntity<OutputTest> response=restTemplate.exchange("http://localhost:8080/test", HttpMethod.GET, entity, OutputTest.class);
OutputTest out=response.getBody();
}
How can I send a single object to restTemplate?

You should tell Spring how to bind the request parameters to SearchFilter. There are multiple approachs to achieve that, The simplest solution is to use ModelAttribute annotation:
#RequestMapping(value="/test", method=RequestMethod.GET)
public Return serverTest(HttpServletRequest req, #ModelAttribute SearchFilter search) throws Exception{
//...
return new OutputTest();
}
Supposing your SearchFilter looks like this:
public class SearchFilter {
private String query;
// other filters and getters and setters
}
If you fire a request to /test?query=something, the SearchFilter will be populated with the sent query parameter. In order to send this request with RestTemplate:
RestTemplate template = new RestTemplate();
// prepare headers
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// request without body, just headers
HttpEntity<Object> request = new HttpEntity<>(headers);
ResponseEntity<OutputTest> response = template.exchange("http://localhost:8080/test?query=something",
HttpMethod.GET,
request,
OutputTest.class);
The other approach i can think of, is to implement a HandlerMethodArgumentResolver for resolving SearchFilter arguments. Also, you can break the SearchFilter apart and use multiple RequestParams.

Related

How to correctly mock Interceptor pre handle method in spring boot integration tests

I'm writing an integration test to an API which receives #RequestAttribute List<String> permissions and HttpServletRequest request as method parameters. There is a custom interceptor which overrides the preHandle() method of HandlerInterceptor. This method receives HttpServletRequest request and HttpServletResponse response as parameters. Based on some logic there are some attributes set in HttpServletRequest request.
I'm writing an integration test where in I send an Http request to the endpoint of the application. I want to mock the interceptor and set these attributes myself.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#RunWith(SpringRunner.class)
public class TradeChargesTest {
#LocalServerPort
public int port;
#MockBean
AppInterceptor interceptor;
// other stuff
#BeforeEach
void initTest() throws Exception {
// Want to write the mock interceptor logic here.
}
public TestRestTemplate restTemplate = new TestRestTemplate();
public String createURLWithPort(String uri) {
return "http://localhost:" + port + uri;
}
}
Test method:
public class UserInfoControllerTest extends TradeChargesTest {
TestRestTemplate restTemplate = new TestRestTemplate();
HttpHeaders headers = new HttpHeaders();
#Test
public void testUserInfoController(){
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity response = restTemplate.exchange(createURLWithPort("Testing/endpoint/user/v5/getUser?from_date=2022-04-01&page=1"), HttpMethod.GET, entity, Object.class);
assertEquals(200, response.getStatusCodeValue());
LinkedHashMap<String, Object> responseBodyMap = (LinkedHashMap<String, Object>) response.getBody();
assertEquals(3, responseBodyMap.get("totalHits"));
assertEquals(1, responseBodyMap.get("page"));
}
}

Spring boot - corrupt authorization header value after RestTemplate.exchange()

I'm having a weird problem, i'm using tokens on Microservice enviroment, I need to call another service with the token already generated.
Call to other service on a Client class... There is the method.
return restTemplate.exchange(URL_REST_METHOD,
HttpMethod.GET,
httpEntity, //HEADER OK
SubscriptionDto.class,
subscriptionId);
To do things easy, I get the previous request, get the token and added to current request.
public class HeaderLoginInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpServletRequest previousRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if(previousRequest.getHeader(AUTHORIZATION) != null){
request.getHeaders().add(AUTHORIZATION, previousRequest.getHeader(AUTHORIZATION));
}
return execution.execute(request, body);
}
}
I attach this interceptor to the Bean RestTemplate
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderLoginInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
The weird thing, after the execution, I see a defect on Authorization header:
Correct one:
Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhdWQiOiJBZGlkYXMgQ2hhbGxlbmdlIiwic3...
Weird after:
Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhdWQiOiJBZGlkYXMgQ2hhbGxlbmdlIiwic3...
I try adding the token directly before call exchange method, same result.
HttpServletRequest previousRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpHeaders headers = new HttpHeaders();
headers.addAll((MultiValueMap<String, String>) previousRequest.getHeaderNames());
HttpEntity<?> httpEntity = new HttpEntity<>(new HttpHeaders());
In both cases, after call method restTemplate.exchange(...) it corrupts.
Any ideas?
Thanks a lot
After a lot of research, the problem is related to charset of RestTemplate.
If I add this line on #Bean configuration, the problem is solved:
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
/*** THE SAVERY LINE ***/
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
/*** ***/
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderLoginInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
After that, works normally.

How to pipe request data from spring MVC controller to Spring RestTemplate

I have a Spring MVC application which takes requests from UI (multiform and json) and it has to post this data to another micro service using Spring RestTemplate. Copying request as string to RestTemplate works fine incase of json content type but doesnt seems to be working incase of multipart.
Here is my sample code
Spring MVC controller:
#Controller
public class MvcController {
#RequestMapping(value = "/api/microservice", method = RequestMethod.POST)
public ResponseEntity<?> callMicroservice(HttpServletRequest request) throws Exception {
RestTemplate rest = new RestTemplate();
String payload = IOUtils.toString(request.getReader());
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", request.getHeader("Content-Type"));
HttpEntity<String> requestEntity = new HttpEntity<String>(payload, headers);
return rest.exchange("https://remote.micrservice.com/api/backendservice", HttpMethod.POST, requestEntity, String.class);
}
}
And here how backend microservice looks like
#Controller
public class RestController {
#RequestMapping(value = "/api/backendservice", method = RequestMethod.POST)
public #ResponseBody Object createService(#RequestParam(value = "jsondata") String jsondata,
#RequestParam(value = "email") String email,#RequestParam(value = "xsltFile", required = false) MultipartFile xsltFile,
HttpServletRequest request) {
// process jsondata
// process xsltFile
// send response
}
}
If you look at MvcController, i am sending payload as string
String payload = IOUtils.toString(request.getReader());
instead, how can I send request data as it is to RestTemplate request so that it works for both string and multipart. If you look at MvcController signature, I do not know what details user would be sending at the sometime I do not know what would be micro service signature. I just need to pipe the data between MvcController and RestTemplate request.
#RequestMapping(value = "/api/microservice", method = RequestMethod.POST)
public ResponseEntity<?> callMicroservice(HttpServletRequest request) throws Exception {
RestTemplate rest = new RestTemplate();
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("jsondata", yourjsondata);
map.add("email", youremail);
map.add("xsltFile", new ClassPathResource(file));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<LinkedMultiValueMap<String, Object>>(
map, headers);
ResponseEntity<String > result = template.get().exchange(
contextPath.get() + path, HttpMethod.POST, requestEntity,
String.class);
}

How can I send a header with void response?

I'm working on a Spring project.
I'm currently doing this.
public ResponseEntity<?> create(#RequestBody final Some entity) {
// persist the entity here
final URI location = uriComponentsBuilder.path("{id}").buildAndExpand(entity.getId()).toUri();
return ResponseEntity.created(location).build();
}
And I found the #ResponseStatus.
#ResponseStatus(HttpStatus.CREATED)
public void create(#RequestBody #NotNull final BaseType entity) {
// persist the entity here
// Location header to where?
}
Is there any way to send Location header with this way?
You can return response entity like below:
return new ResponseEntity<>(location, HttpStatus.CREATED);
or
HttpHeaders headers = new HttpHeaders();
headers.add(location);
return new ResponseEntity<>(headers, HttpStatus.CREATED);
Try this. It returns your preferred header and status, without body.
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT)
#RequestMapping("/teapot")
public HttpHeaders dummyMethod() {
HttpHeaders h = new HttpHeaders();
h.add("MyHeader", "MyValue");
return h;
}

Spring RESTful endpoint not asynchonous

I have an issue with an endpoint that is not behaving as I would expect. Basically, it follows the flow the way I would expect and it hits the return statement BEFORE it fires the callback methods. So, all seems good.
However, when I test this in Postman, the method still hangs. It blocks and does not return a response until the callback onSuccess is executed. Is this how this should behave or am I missing something?
#RequestMapping(value = "/async", method = RequestMethod.POST)
#ResponseBody
public DeferredResult<String> createAsync(#RequestBody Input input) throws CombineException {
AsyncRestTemplate restTemplate = new AsyncRestTemplate();
DeferredResult<String> result = new DeferredResult();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<?> entity = new HttpEntity<>(input , headers);
ListenableFuture<ResponseEntity<Input>> future = restTemplate.exchange("http://localhost:8080/myLongRunningEndpointIDontWantToWaitFor", HttpMethod.POST, entity, Input.class);
future.addCallback(new ListenableFutureCallback<ResponseEntity<Input>>() {
#Override
public void onSuccess(ResponseEntity<Input> response) {
System.out.println("Success");
result.setResult(response.getBody().toString());
}
#Override
public void onFailure(Throwable t) {
System.out.println("FAILED");
result.setErrorResult(t.getMessage());
}
});
System.out.println("RETURNING...");
return result;
}

Resources