Spring Webflux Mono<Void> always responds with successful response - functional-programming

I have a endpoint that takes id parameter and send delete product api to delete.
productService.delete also returns Mono. The problem is when productService.delete method returns mono error, the endpoint always responds with http 200. And I can see error log about this mono error but my handler method responds http 200.
I have a AbstractErrorWebExceptionHandler to handler exception in my api. But error handler can't handle this problem because of Mono. When exceptions occurs in downstream, Spring webflux should aware of this error and does not respond with http 200.
public Mono<ServerResponse> deleteProduct(ServerRequest request) {
String id = request.pathVariable("id");
Mono<Product> productMono = this.repository.findById(id);
return productMono
.flatMap(existingProduct ->
ServerResponse.noContent()
.build(productService.delete(existingProduct))
);
}
Btw in the source code, it says the response will be committed when given publisher completes. But how about error complete ? I think Spring webflux does not check it is error signal or not. Just check for mono completes or not.
* Build the response entity with no body.
* The response will be committed when the given {#code voidPublisher} completes.
* #param voidPublisher publisher publisher to indicate when the response should be committed
* #return the built response
*/
Mono<ServerResponse> build(Publisher<Void> voidPublisher);
Thank you in advance.

The problem is caused by using voidPublisher. If you create ServerResponse with void publisher, it simply will return http 200 even your downstream complete with error signal. It just does not care how your stream finish, it just care about completion of downstream.
And if you want handle downstream error when you build your response just simple use
ServerResponse.noContent()
.body(productService.delete(existingProduct), Void.class)
Now whenever any error occurred in downstream, server will respond with error.

Related

Spring WebFlux Broken Stream Error Handling

I'm struggling to find any good examples on how to implement error handling with Spring WebFlux.
The use case I want to handle is notifying HTTP clients that a stream has terminated unexpectedly. What I have found it that with the out of the box behaviour, when a stream is terminated, for example by raising a RuntimeException after x items have been processed, is handled too gracefully! The client is flushed all items up until the exception is raised, and then the connection is closed. As far as the client is concerned the request was successful. The following code shows how this has been setup:
public Mono<ServerResponse> getItems(ServerRequest request) {
Counter counter = new Counter(0);
return ServerResponse
.ok()
.contentType(MediaType.APPLICATION_STREAM_JSON)
.body(operations.find(query, Document.class, "myCollection")
.map(it -> {
counter.increment();
if(counter.getCount() > 500) {
throw new RuntimeException("an error has occurred");
}
return it;
}), Document.class);
}
What is the recommended way to handle the error and notify the HTTP client that the stream terminated unexpectedly?
It really depends on how you'd like to communicate that failure to the client. Should the client display some specific error message? Should the client reconnect automatically?
If this is a "business error" that doesn't prevent you from writing to the stream, you could communicate that failure using a specific event type (look at the Server Sent Events spec).
Spring WebFlux supports ServerSentEvent<T>, which allows you to control various fields such as event, id, comment and data (the actual data). Using an Flux::onErrorMap operator, you could write a specific ServerSentEvent that has an "error" event type (look at the ServerSentEvent.builder() for more).
But this is not transparent to the client, as you'd have to subscribe to specific events and change your JavaScript code otherwise you may display error messages as regular messages.

Spring Job Response To Client

Spring batch job runs to completion successfully with following code:
#RestController
#RequestMapping(value = "api/jobs")
public class JobLaunchingController {
#Autowired
private JobOperator jobOperator;
#RequestMapping(value = "/pay/{paymentPeriod}", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.ACCEPTED)
public void launchPaymentJob(#PathVariable Integer paymentPeriod) throws Exception {
this.jobOperator.start("paymentJob", String.format("paymentPeriod=%s,time=" + System.currentTimeMillis(), paymentPeriod));
}
}
I am using JavaFX client where the endpoint is used to send request for the job to be launched. Basically, with the help of jersey client http request is sent like so
Client client = ClientBuilder.newBuilder().build();
WebTarget webTarget = client.target(getBaseUri()).path(path);
Response response = webTarget.request().get(Response.class);
The problem is i don't have a way for the client to know when the job is complete. How can client be notified by server when job is done
Any help is highly appreciated.
This is more of a architectural question than coding. You can solve this by multiple ways, I will suggest you below three
Simplest -> If your jobs doesn't take long - make your client wait for the outcome of job. Send them final status back in response.
Send job reference number immediately to client and introduce another endpoint where client can check with reference number if jobs is done - This involves your client having some polling mechanism.
Use something like Server Sent Events/Websockets - Thanks cerp0
How can client be notified by server when job is done?
Try to use Websokets to send data from server to client. Here is a pretty good guide how to do it with Spring.
On the client side I believe you can use this approach: https://github.com/nickebbutt/stomp-websockets-java-client

spring integration handle exception after splitter

I am new to Spring integration.
If I have a request coming in with batch payload(json array)
and I use splitter to split it into jsonobject,
and then I do validation.
If some of the validation failed and throw exception into error channel.
How can I make a response to client indicating some of the jsObject failed
and some works?
not sure handler at errorChannel gonna help since the validation result comes async into errorChannel.
And if I call the gateway like this, how can I construct a validation Result for the whole payload with validation status for each jsObject inside?
Future<validationResult> r = gateway.send(...)
(gateway just forward the request to following endpoint right away)
You have to take a look into Aggregator EIP: http://www.enterpriseintegrationpatterns.com/patterns/messaging/Aggregator.html, http://docs.spring.io/spring-integration/reference/html/messaging-routing-chapter.html#aggregator.
So, all your objects are send for the validation and their results (good or bad) send to the <aggregator> to build a single validationResult for the reply to that gateway.

How to get the HTTP-Status from a webMethods com.wm.net.NetException?

How to get the HTTP-Status from a webMethods com.wm.net.NetException?
Is there a way to get the http status code from within the catch block of a java service after calling the pub.client:http service?
If you invoke the pub.client:http from within a flow service, you'll notice that it doesn't throw an exception. For example, a "403 Forbidden" error, will not throw an exception. Instead, it will output to the pipeline a header document.
Within the header document you will find the http status:
When you invoke pub.client:http from within a java service then the invocation is suppose to return an IData object. From that object you should be able to extract the status field using IDataUtil.
So, when you evaluate that the status is not OK, you can throw a ServiceException which will be caught by the flow try/catch.
Hope this helps!

Spring MVC Returns Response Before Completion of Controller Method

I have the following method which is returning an incorrect response to the browser before the method is even complete. This is in Spring 3.2.
#RequestMapping(value="/process1/createEditContract/validate", method=RequestMethod.POST)
public #ResponseBody StatusResponse validateProcess1(#ModelAttribute("contractEditForm") #Valid Process1CreateEditContractDTO dto, BindingResult bindingResult) {
StatusResponse response = new StatusResponse();
response.setSuccess(true);
if (bindingResult.hasErrors()) {
log.debug("Errors found. Processing status response");
response.setSuccess(false);
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fe: fieldErrors) {
response.getMessages().add(messageSource.getMessage(fe, null));
}
}
return response;
}
StatusResponse is a simple object that a javascript function in the JSP reads to generate a Javascript alert stating whether the action was successful or errors occurred. The method makes it all the way through, but as soon as it tries to write the response, I get this:
java.net.SocketException: Software caused connection abort: socket write error
I've been stuck for a day now, any help would be appreciated.
UPDATE
I rolled back from Spring 3.2 to Spring 3.1, and the wording of the error message changed enough to give me more information.
Basically, I'm getting now seeing this:
IllegalStateException: Response already committed
What I don't see is what is causing the response to commit so quickly. Maybe a conflict with the OpenSessionInViewFilter?
This error can occur when the local network system aborts a connection, such as when WinSock closes an established connection after data retransmission fails (receiver never acknowledges data sent on a datastream socket).". See this MSDN article. See also Some information about 'Software caused connection abort.
To prove which component fails I would monitor the TCP/IP communication using wireshark and look who is actaully closing the port, also timeouts could be relevant.
The javascript runs in browser, and your controller runs on server. You cannot pass a complex object from the controller to the javascript without converting it to a textual format such as xml or json.
So you should :
choose a format (say json)
add a produces="application/json" in your RequestMapping annotation
do generate json in your controller method

Resources