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.
Related
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.
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.
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.
I've seen this, however I am having this issue now.
If I change my controller to return a ResponseEntity<?> the body comes through fine, but if it returns DeferredResult<ResponseEntity<?>> then I get my headers and status code but no response body.
Any help will be greatly appreciated
Use of DeferredResult is not simply a matter of changing the controller's response type. A controller which returns a DeferredResult will likely delegate handling of the request to a thread pool (or some async mechanism) and whatever actually performs the request must set the response on the DeferredResult. Something like this:
In the controller:
DeferredResult deferredResult = new DeferredResult<>();
// pass deferredResult into a runnable task (or something similar)
// which is scheduled for execution via a thread pool (or something similar)
return deferredResult;
In the task which executes the request on behalf of the controller:
// do some work
// ...
deferredResult.setResponse(responseBody);
#JsonView
how can use like parameter from request:
#JsonView(header="range")
when response value,read request header range to exclude/include some field
JsonView provides "static" view mapping. so for your dynamic behaviour you can do like this:
// actual request handling is happened here
private ResponseEntity<SomeObject> processRequest();
// request handling with view limit in result
#JsonView(YourDefinedView.class)
#RequestMapping(value = "/request", headers={"range=include"})
public ResponseEntity<SomeObject> processWithView() {
return processRequest();
}
// request handling without view (no headers specified)
#RequestMapping(value = "/request")
public ResponseEntity<SomeObject> processWithoutView() {
return processRequest();
}
this will map your client to same request url, but depending on header it will provide view or not. Than you can create a set of methods, that will be using different #JsonView depending on headers information.
But with this you will limit only the data transfered to client, and the whole data load will happen on server. For example with database and JPA, if you would like not to fetch from database all that data you will end with javax.persistence.NamedEntityGraphs, which will change the general logic of your application - and will at the end of the day produce 2 different methods.
And if you would like to expose custom header with list of fields, to be serialized - custom DTO object, or Map<String, Object> (ugly-ugly-ugly) or custom HandlerMethodReturnValueHandler comes to your help.