Why can't subscribe the request in reactor-netty? - reactor

I just want access the Http content in reactor-netty project. But the result is null.
Code is below.
DisposableServer server =
HttpServer.create()
.host("localhost")
.port(8000)
.route(routes ->
.post("/echo",
(request, response) ->
{ request.receive()
.retain()
.aggregate()
.asString()
.subscribe(System.out::println);
return response.sendString(Mono.just("hello"));})
.bindNow();
I can't get the rerult in the console.
Could I access the request as what I do in the code?
Anyone can help? Thanks.

You return the response before the request data is received, so Reactor Netty will drop any incoming data that is received AFTER the response is sent.
I don't know your use case but changing the example to this below, you will be able to see the incoming data:
DisposableServer server =
HttpServer.create()
.host("localhost")
.port(8000)
.route(routes ->
routes.post("/echo",
(request, response) ->
response.sendString(request.receive()
.retain()
.aggregate()
.asString()
.flatMap(s -> {
System.out.println(s);
return Mono.just("hello");
})))
)
.bindNow();

Related

Why is the log statement in Spring WebClient subscribe() block not invoked?

I am playing around with the spring webclient to send emails via an api. The mails get send, however I am trying to do this without blocking and I want to log the successful response (or error). As I understood it so far, I could do this in the subscribe block. However, I do not manage to get the logging (or anything else) within the subscribe block to work.
MailClient.kt
fun sendMail(mail: Mail): Mono<String> {
return getWebClient()
.post()
.uri("uri")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(..)
.retrieve()
.bodyToMono(String::class.java)
//.timeout(Duration.ofSeconds(10))
}
Service function which uses MailClient:
logger.info("Sending mail due to failure with id = ${failure.id}, error: $errorMessage")
mailClient.sendMail(
Mail(..)
)
.subscribe(
{ response -> logger.info("success: $response") },
{ error -> logger.error("error: $error") })

How to use Spring WebClient to make multiple calls simultaneously and get response separately?

I have to execute multiple API calls simultaneously which are independent of each other:
Mono<Response1> response1= this.webClient
.post()
.uri(requestURI1)
.body(Flux.just(request.getRequestBody()), ParameterizedTypeReference<T>)
.exchangeToMono(response -> {
return response.statusCode().equals(HttpStatus.OK)
? response.bodyToMono(ParameterizedTypeReference<T>)
: response.createException().flatMap(Mono::error);
});
Mono<Response2> response2= this.webClient
.post()
.uri(requestURI2)
.body(Flux.just(request.getRequestBody()), ParameterizedTypeReference<T>)
.exchangeToMono(response -> {
return response.statusCode().equals(HttpStatus.OK)
? response.bodyToMono(ParameterizedTypeReference<T>)
: response.createException().flatMap(Mono::error);
});
Mono<Response3> response3= this.webClient
.post()
.uri(requestURI3)
.body(Flux.just(request.getRequestBody()), ParameterizedTypeReference<T>)
.exchangeToMono(response -> {
return response.statusCode().equals(HttpStatus.OK)
? response.bodyToMono(ParameterizedTypeReference<T>)
: response.createException().flatMap(Mono::error);
});
How can I get the response of above api calls in separate Objects and at the same time, they should be executed in parallel? Also, after executing these above calls and putting the data in separate objects, say, Response1, Response2, Response3, I want to execute another API call which consumes these responses Response1, Response2, Response3.
I have tried to use Flux.merge but this will merge the responses in single object which is not correct. Also read about Mono.zip, but these all are used to combine the responses which I don't want.
EDIT:
Mono.zip works perfectly.
I have a follow up question which I have mentioned in comments but posting it here also.
So I have implemented like this:
Mono.zip(rs1, rs2, rs3).flatMap(tuples -> {
//do something with responses
return Mono.just(transformedData)
}).flatMap(transformedData -> {
//here another webclient.post call which consumes transformedData and return actualData in form of Mono<actualData>
Mono<actualData> data= callAPI;
return data;
});
Now this response is propagated to rest layer in form of Mono<actualData> and I am getting this in response: {
"scanAvailable": true
}
To combine publishers in parallel you can use Mono.zip that will return TupleX<...> and will be resolved when all publishers are resolved.
Mono<Tuple3<Response1, Response2, Response3>> res = Mono.zip(response1, response2, response3)
.map(tuples -> {
//do something with responses
return transformedData;
})
.flatMap(transformedData -> {
//here another webclient.post call which consumes transformedData and return actualData in form of Mono<actualData>
return callAPI(transformedData);
});
Depending on the error handling logic you could consider zipDelayError
As I mentioned in comment one of the key things with reactive API is to differentiate sync and async operations. In case of sync transformation, chain it with .map and use .flatMap or similar operator in case you want to chain another async method.

How can i handle connection and reconnect if connection got closed?

I need this client stay connected for long, How can i make sure about connection? because the issue was in connection, so i am updating my question. what should i do if server close connection? or if client close connection? how can i handle it and reconnect client to the server?
public void consumeServerSentEvent() {
WebClient client = WebClient.create("http://localhost:8080/sse-server");
ParameterizedTypeReference<ServerSentEvent<String>> type
= new ParameterizedTypeReference<ServerSentEvent<String>>() {};
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-sse")
.retrieve()
.bodyToFlux(type);
eventStream.subscribe(
content -> logger.info("Time: {} - event: name[{}], id [{}], content[{}] ",
LocalTime.now(), content.event(), content.id(), content.data()),
error -> logger.error("Error receiving SSE: {}", error),
() -> logger.info("Completed!!!"));
}
According to documentation retrieve() returns Mono of ClientResponse, but for your case you need to consume Flux of the body.
Try some thing like this:
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-sse")
.retrieve()
.flatMapMany(response -> response.bodyToFlux(type));

Spring WebClient async callback not called when http server response 404

A problem I encountered is as titled, and the code is below:
Mono<Account> accountMono = client.get()
.uri("accounturl")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.flatMap(response -> {
if (response.statusCode().equals(HttpStatus.OK)) {
return response.bodyToMono(Account.class);
} else {
return Mono.empty();
}
});
accountMono.subscribe(result -> callback(result));
```
Server response 404. I try to create an empty Account, but the callback() is not called. It looks like the empty Mono is not emitted.
Server response 404, I try to create a empty Account
You're not creating an empty Account. You're returning an empty Mono, i.e. a Mono that will never emit anything.
If you want to return a Mono which emits an empty Account, then you need
return Mono.just(new Account());

How do I handle a failed post operation using Giraffe?

How do I handle a failed post operation using Giraffe?
What's the recommended practice for a failed post operation using Giraffe?
let private registrationHandler =
fun(context: HttpContext) ->
async {
let! data = context.BindJson<RegistrationRequest>()
let response = register data |> function
| Success profile -> profile
| Failure -> ???
return! json response context
}
Specifically, if the server fails to write data to some database, what should I return to the client (that will compile).
The handler has to return something, but it doesn't always have to be the same serialized object. I've only had a quick glance at Giraffe, but using similar approach from Suave with Giraffe's examples here: https://github.com/dustinmoris/Giraffe#setstatuscode, I would do something like this:
type ErrorResponse = { message: string; ... }
let private registrationHandler =
fun(context: HttpContext) ->
async {
let! data = context.BindJson<RegistrationRequest>()
match register data with
| Success profile ->
return! json profile context
| Failure ->
let response = { message = "registration failed"; ... }
return! (setStatusCode 500 >=> json response) context
}

Resources