Kotlin/Spring - Fowarding post request from one service to another - spring-boot

We had a spring web-app that used to handle all front and back end logic. As we need to scale we have split that into two microservices. How would I go about 'forwarding' a post request to another url (including its body and authentication headers). For example:
microservice1 has an endpoint /api/doSomething
microservice2 has an endpoint /privateUrl/doSomething
I want the user to hit the endpoint on microservice1 which will post to microservice2 and return the result.
I have tried with RestTemplate without much luck, i keep getting error "Could not write JSON: No serializer found for class..." from microservice1, i suspect this is because microservice1 doesnt know how to parse the body object microservice2 requires.
microservice1:
#PostMapping("/api/DoSomething")
fun postIT(request: HttpServletRequest, #PathVariable one: String, #PathVariable two: String){ ...}
microservice2:
#CrossOrigin
#PostMapping("/privateUrl/doSomething")
fun postIT(request: HttpServletRequest, #RequestParam one: String, #RequestParam(required = false, defaultValue = "true") two: String,#RequestBody it: IT) { ... }
I know i can parse the entire request in microservice1 and then send it to microservice2, however is there a way to just 'forward' the http request to a new url?

I am not sure what you mean , but if you want to communicate from one server to another you can always use rest template (or any other) and send http request from one service to another, you can see examples here
https://www.tutorialspoint.com/spring_boot/spring_boot_rest_template.htm
https://spring.io/guides/tutorials/bookmarks/
take a look it should work for you.

Related

spring cloud gateway, avoid routing to a uri

I'm looking for a way to execute some filters and predicates on a request, and at the end simply return a response to the user, instead of routing it to a specific URI.
For example, a user is calling /auth/token and my gateway has a filter that generates a token and transforms the body of the response (using the ModifyResponseBodyGatewayFilterFactory).
When adding a filter that simply returns response.setCompleted(), the body returns empty and the status code is always 200.
return (exchange, chain) -> {
return modifyResponseBodyGatewayFilterFactory.apply(c -> c.setRewriteFunction(Object.class, String.class, SomeBody))
.filter(exchange, chain)
.then(exchange.getResponse().setComplete());
}
How can I return a specific body to the user, without routing to a URI?
Thanks in advance!
I couldn't find a solution, so instead, I've created a web flux controller for this specific request.
This is a good enough solution for me.

ClientResponse(org.springframework.web.reactive.function.client.ClientResponse) showing inconsistency across the classes while using WebClient

I have started using WebClient(org.springframework.web.reactive.function.client.WebClient) for calling rest services. I have 2 classes say Utility.java and ServiceImpl.java.
ServiceImpl.java is where I use WebClient. A post call I am making looks like -
ClientResponse response = webClient.post()
.uri(path)
.body(Mono.just(inputDTO),InputDTO.class)
.exchange()
.block();
(ClientResponse above is org.springframework.web.reactive.function.client.ClientResponse)
(I am using exchange instaed of retrive because I want headers as well as status code)
Now trying to convert this response into some DTO - ResponseDTO.
Mono<ResponseEntity<ResponseDTO>> mono = response.toEntity(ResponseDTO.class);
ResponseEntity<ResponseDTO> resEntity = mono.block();
ResponseDTO myObj = resEntity.getBody();
So myObj is an object of ResponseDTO class.
The issue is - when I perform the conversion of 'response into ResponseDTO.java' in my utility class, I get myObj = null. But if I do it in my ServiceImpl.java (just after calling post API), it returns the proper body (ResponseDTO object).
The same issue occurs even if I perform the conversion and post call operation in two different methods in the ServiceImpl.java.
Do I need to configure something here?
I figured out what was the issue.
After calling REST api, body in the response if flushed out after I read it from the response for the first time. I had a Sysout statement in service implementation class where I was reading the body content.
Recommendation: Read the body content only once and store it in a variable. Use it wherever required.

Feign get request with body

For some reason I need to call a GET method API and pass json request body for it. I really couldn't find an example for it. I wonder if it is even supported using feign.
How can I do that using feign?
Yes, Feign supports it. You can do the same as with POST requests:
#FeignClient(name = "clientName", url = "http://localhost:8888")
public interface SampleFeignClient {
#GetMapping("/remote")
String test(#RequestBody SampleRequestBody sampleRequestBody);
}
But be aware: a lot of servers ignore body or even refuse that kind of "non-standard" requests completely (GET or HEAD with request bodies).
According to the documentation the correct way to do it would be to use the #SpringQueryMap annotation.
#FeignClient(name = "clientName", url = "http://localhost:8888")
public interface SampleFeignClient {
#GetMapping("/remote")
String test(#SpringQueryMap SampleRequestBody sampleRequestBody);
}
You can find more information here

Spring receiving empty body

I'm using Spring for my backend. I have the following code:
#RequestMapping(value = "/test", method = RequestMethod.POST)
#CrossOrigin
public void test(HttpServletRequest request) {
System.out.println(request.getParameterMap().size());
System.out.println(new JSONObject(request.getParameterMap()));
}
When I send JSON data using Postman, I get a map of all the parameters I've sent.
But when I'm making the same call from my website, I get an empty map with size 0. I do not get any error or exception on both front and back sides.
What could be the reason?
Thank you
Most probably getParameterMap() is really empty in your case, because parameters are not passed as query, but as a body (content) of HTTP request when it is sent from your web-site.
It can also be affected by the Content-Type and Accept headers of the HTTP request.
According to official documentation ServletRequest.getParameterMap() returns:
For HTTP servlets, parameters are contained in the query string or posted form data.
Usually "posted form data" implies: HTTP header Content-Type: application/x-www-form-urlencoded and URL encoded name-value pairs of parameters in the content of HTTP request.
If your web-site sends application/json, any other content type, or does not define content type at all, it might not be properly mapped into the request parameters by servlet container. In this case you should look into the body of HTTP request (ServletRequest.getReader()) to get the payload, or let Spring MVC do that (e.g. #RequestBody annotation).

How to get Person instead of Mono<Person>?

On the retrieve code below, how to get Person instead of Mono or how to get Person from Mono, please ?
23.2.3 Request and Response Body Conversion
The response body can be one of the following:
Account — serialize without blocking the given Account; implies a synchronous, non-blocking controller method.
1.7.1. Retrieve
WebClient client = WebClient.create("http://example.org");
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
Once you have a Mono<Person> instance available, you have two choices:
compose that reactive type (i.e. use operators available on that type) and use it to save that data in a datastore, serve it as a HTTP response body, etc
or call Person person = result.block() on it, which blocks. So you should not do that in a reactive application because this might completely block the few threads available to your application.

Resources