Reactive Spring - Request body is missing - - spring-webflux

I have a Reactive Rest Controller that makes a POST call along with Flux<> Request Body.
I want to do sequential steps in this Post call
Validate Request Body input
Delete all rows based on one field inside Request Body.
Save all data from this Flux Request Body
Return all updated saved data from Flux to Controller
Sample Code
public Mono<Boolean> update(Flux<RequestBody> body, Long id) {.
return body.
.flatMap(d -> this.repo.deleteCriteria(id, d.getValue);
.thenMany(body)
.flatMap(m-> {mapper to convert from DTO to Entity});
.flatMap(s -> this.repo.save(s.name, s.data)
.then(c -> this.repo.findById(id).count)
.then(n-> n.intValue() > 0 ? Mono.just(true) : Mono.just(false));
}.
Controller code -
#ResponseStatus(HttpStatus.CREATED).
#PostMapping("/abc").
public Mono<ResponseEntity<Void> update(Long id, Flux<Body> body) {
Mono<Boolean> onSuccess = this.service.update(id, body);
onSuccess
.log()
.flatMap(b -> b ? Mono.just(new ResponseEntity<>(HttpStatus.CREATED) :
Mono.just(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
I see in Logs that Delete statement getting executed n times. Ideally I wanted it only one time to run but I only know flatMap().
But after delete statement I see below error in logs.
"Error - 400 Bad Request - Request body is missing ....."
FYI - Before when I only had a delete statement and I was not fetching value from Flux Request Body everything was working fine.
Please let me know if there is a way to
read values from Request Body and do validations.
perform multiple operations on this Request body like Delete, save and find by reading some values from the RequestBody.
Any help appreciated :)

Related

How to read RequestBody by PipeReader and return start position of stream to zero (Net core 6)

I need access to RequestBody inside OnActionExecuting or OnActionExecuted filter, but I can not found concrete example how to use PipeReader to read full request body stream and return stream position to zero (in order to read request parameters by ControllerBase - if I read body OnActionExecuting or firstly return position to zero if I read in OnActionExecuting).
In my attribute Body always empty.
However, API parameters is present.
Or maybe there is another way to receive Request Body in Action Filter for ControllerBase?
From your description, I think you wanna get request body in multiple times in asp.net core. But in asp.net core, the request can not be read once it is consumed. If you want to read the request body multiple times, you need to set:
context.Request.EnableBuffering()
Then to read the body stream you could for example do this:
string bodyContent = new StreamReader(Request.Body).ReadToEnd();
On the safer side, set the Request.Body.Position reset to 0. That way any code later in the request lifecycle will find the request body in the state just like it hasn’t been read yet.
Request.Body.Position = 0;
So you can set this code in either OnActionExecuting or OnActionExecuted method.
public void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Request.EnableBuffering();
string bodyContent = new StreamReader(context.HttpContext.Request.Body).ReadToEnd();
context.HttpContext.Request.Body.Position = 0;
}
But please note that, Model binding happens before action filter, So Model binding will consume request body first and you can not read it in your action filter, You need to custom model binding and Apply the above configuration to it.

How can I get webflux response body twice? Store some data from request/response

I created a filter to log some important data from the request and response...
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
URI uri = request.url();
HttpMethod method = request.method();
return next.exchange(request).onErrorResume(err -> {
writeToFile(method, uri, 500, "", true);
return Mono.error(err);
}).flatMap((response) -> {
return response.bodyToMono(String.class).flatMap(body -> {
writeToFile(method, uri, response.rawStatusCode(), body, false);
return Mono.just(response);
});
});
}
The idea of this filter is to register some data and let the rest work exactly the same.
Expected: This was my attempt to keep the rest of the code untouched and just include a filter to store some data apart from the normal flow.
Actual: It works, except when the real code calls bodyToMono, it returns null
At some point of the normal flow, I am doing something similar to this:
return client.get().uri(...).exchangeToMono((response) -> {
return response.bodyToMono(MyObject.class);
})
It works flawlessly when I remove the filter.
So I discovered I can't call bodyToMono twice the way I am using probably because the first call is consuming the body and then it is empty in the second call.

Is there a way for postman to read the response data from a GET request and then use an IF THEN statement to run a POST request?

I am trying to run a script where postman sends a Get request, and if the get response contains a certain variable to then run a Post request. How do i do this?
(also is there a way to run a get request hourly)
In pre-requisite add:
// set initial value
const method = pm.variables.get("method")
// set initial value as GET if method is undefined
method ? null : pm.variables.set("method", "GET")
// Set this as method
pm.request.method =method
in test script add :
// the condition check
if (pm.response.json().somevalue === "somevalue") {
//then change the method
pm.variables.set("method", "POST")
//call the same request again using setNExtRequest
// pm.info.reqeustName gives current request's name
postman.setNextRequest(pm.info.requestName)
}

How to avoid Expect msg with HTTP 2.0.0 in elm?

In elm's Http 1.0.0 package, I could send a custom request like:
post : Endoint -> List (Http.Header) -> Http.Body -> Decoder a -> Http.Request a
post url headers body decoder =
Http.request
{ method = "POST"
, headers = headers
, url = url
, body = body
, expect = Http.expectJson decoder
, timeout = Nothing
, withCredentials = False
}
With the post function I wrote above, I can simply call it with, say, a Decoder String, and after the Http request sends, the response string will be decoded and returned. period. No need to create a Msg like:
type Msg
= GotText (Result Http.Error String)
And no need to write a branch in update to handle this Msg.
However, as of Http 2.0.0, the expect argument is of type Expect msg, not Expect a, meaning that writing the Msg variation and additional branch to update will now be required.
I am writing an Api.elm file which makes Http requests. However, this means that now it will have to have its own Msg type and update function to run after these requests respond.
I used to think that Home.elm should only respond to messages from Home.Msg and Home.update not Api.Msg and Api.update. Am I wrong? Should Api.elm have its own Msg type and update function that changes other pages? Or would there be a better way to do this?
To clarify what I was explaining in my question:
A custom request in Elm's HTTP 2.0.0 package looks like this:
request :
{ method : String
, headers : List Header
, url = String
, body = Body
, expect = Expect msg
, timeout = Maybe Float
, withCredentials = Maybe String
}
-> Cmd msg
Wheras in Http 1.0.0 it looked like this:
request :
{ method : String
, headers : List Header
, url : String
, body : Body
, expect : Expect a
, timeout : Maybe Float
, withCredentials : Bool
}
-> Request a
The difference is that using the custom request from HTTP 2.0.0, I needed to pass-in a Msg to use this request.
Now, my problem was: I was using an Api.elm file. Every time I needed to issue an HTTP request, I would call Api.request arg1 arg2... from, say, Login.elm.
Since the request function in Api.elm demanded a Msg type, for, in this case, a login request I thought I would have to define a Msg of GotLogin within Api.elm and then handle how GotLogin would update Login.elm by writing an update branch for GotLogin within Api.elm.
However, I could just define a GotLogin Msg in Login.elm and pass that into Api.request. Since I've defined GotLogin in Login.elm, I would put a GotLogin branch in the update function of Login.elm, instead of Api.elm.
This also applies for any other request type from any other page (Signup.elm, Home.elm,...), meaning that Api.elm, should not have its own update function that updates other pages.
The whole point of Login.elm having its own update function, is that it should only be affected by the branches of its own update function, not ones from Api.elm.

How to correctly handle the case where a stream is empty

How do I perform operations that have side effects in response to a stream being empty? For example, to handle the case where a client sends an empty body, and we read it with .bodyToMono(...).
See for example:
public Mono<ServerResponse> createEventDetails(ServerRequest request) {
return request.principal().flatMap(principal -> {
UserProfile userProfile = (UserProfile) ((UsernamePasswordAuthenticationToken) principal).getCredentials();
final String id = request.pathVariable("id");
final UUID eventId = UUID.fromString(id);
return request.bodyToMono(CreateEventDetailsRequest.class)
.map(body -> new CreateEventDetailsCommand(eventId, userProfile, body.getObjectId(), body.getDocumentUrls()))
.flatMap(command -> {
createEventDetailsCommandHandler.handle(command);
return ServerResponse
.created(URI.create(eventId.toString()))
.body(BodyInserters.fromObject(new CreateEventDetailsResponse(eventId)));
})
.switchIfEmpty(ServerResponse.badRequest().syncBody(new Error("missing request body for event id: " + id)))
.map(response -> {
LOG.error("POST createEventDetails: missing request body for event id: " + id);
return response;
});
});
}
This returns the desired 400 response in the case that the client omits the request body, but the log message is always printed, even for successful requests.
My best guess as to why this occurs, is that .switchIfEmpty(...).map(...) is executed as one unit by the framework, and then the result ignored.
How do I handle this case?
There are a few ways to do this. In the case above you can just add the logging to the switch
e.g.
.switchIfEmpty(ServerResponse.badRequest().syncBody(new Error("missing request body for event id: " + id))
.doOnNext(errorResponse -> LOG.error("POST createEventDetails: missing request body for event id: " + id)
)
another approach could be sending an Error signal back and in your handler catch and log it.
You can have a look here https://www.baeldung.com/spring-webflux-errors
Some light reading can be found here https://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/javadoc-api/org/springframework/web/server/WebHandler.html
Use HttpWebHandlerAdapter to adapt a WebHandler to an HttpHandler. The
WebHttpHandlerBuilder provides a convenient way to do that while also
optionally configuring one or more filters and/or exception handlers.
You can view a sample handler in ResponseStatusExceptionHandler