We are making use of WebClient Builder call for communication among micro services in our project.It's springboot-thymleaf-webflux based application.Everything is running fine till now but we got a requirement from Client to change GET call to POST call in thymleaf controller.After making these changes at UI and Backend,Thymleaf calls are working fine but one of the XHR i.e post call from UI-- is giving bad request error for WebClient call to second microservice for first click and working for second click.I compared header and request for both click did not find any difference.I am not able to understand why WebClient behaving abnoramlly first time and working fine second time.
Below is code snippet for WebClient call and it is routing to WebClientResponseException without hitting to second microservice by saying BAD Request for http://second-microservice -eureka-address/endpoint-url
* Submitting xyz
* @param submitFlowRequest
*/
@Override
public Mono<ApiResponse<SubmitResponse>> submitFlow(SubmitFlowRequest submitFlowRequest,
Map<String, String> headers) {
long startTime = System.currentTimeMillis();
String uri = propertyConfig.getAggregationService()
+ propertyConfig.getAggregationSubmitCCInfoURL();
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(uri);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT);
MultiValueMap<String, String> clientHeaders = buildHeaders(headers);
return webClientBuilder.uriBuilderFactory(factory).build().post()
.headers(httpHeaders -> httpHeaders.addAll(clientHeaders)).accept(MediaType.APPLICATION_JSON)
.syncBody(submitFlowRequest).retrieve().bodyToMono(ApiResponse.class)
.onErrorMap(ConnectException.class,
error -> new VzwRuntimeException(ErrorCodeEnum.V404.toString(), Constants.OPP_TC_SYSTEM_ERROR,
(Constants.CONNECTION_FAILURE_TEXT + Constants.AGGREGATION)))
.onErrorMap(WebClientResponseException.class,
error -> new VzwRuntimeException(ErrorCodeEnum.V404.toString(), Constants.OPP_TC_SYSTEM_ERROR,
(Constants.CONNECTION_FAILURE_TEXT + Constants.AGGREGATION)))
.flatMap(res -> {
Audit apiAudit = Audit.builder().apiUrl(uri).request(LoggerUtil.asJson(submitFlowRequest))
.response(LoggerUtil.asJson(res))
.executionTime(String.valueOf(System.currentTimeMillis() - startTime))
.headers(LoggerUtil.asJson(clientHeaders)).transactionType(res.getData()!=null?mapper.map(res.getData(), SubmitResponse.class).getTransactionType():"").build();
LoggerUtil.logExternalApiCalls(apiAudit);
return Mono.just((ApiResponse<SubmitResponse>) res);
});
}
Please provide me some lead.I am tired of finding solution for this.
Comment From: rstoyanchev
It sounds like you're getting a 400 error from the second microservice. You'll need to find out what is the reason for that. You should examine check the exact request and response on the wire between microservice 1 and 2. You can do that with wire logging on Reactor Netty. If that doesn't reveal the problem you can also debug or check logging from the microservice 2 in order to find why it returns 400.
Only when you know what the reason for the 400 is, you can begin to think about the root cause. At this stage there is not enough information to say that the WebClient is behaving abnormally.
There is very little I can do with the given information, and there isn't enough evidence of an issue in the framework, so I'm going to close this for now. If you find out more and you do suspect an issue in the framework feel free to comment further. However keep in mind that either the problem needs to be clear or I'll need instructions or a sample for how to reproduce it.
Two small notes:
- The WebClient.Builder is used to create a WebClient instance which can then be used for many calls. The builder should not be used for every request.
- The flatMap at the end could be just map and instead of wrapping the response with Mono.just, simply return the response.
Comment From: manuj20
About small notes mentioned by you.
- I agreed to same and tried this as well but there is already open issue as it failed along with
@LoadBalancedannotation and failed to resolve eureka instance . 2.I agreed the same but this change will not resolve my problem.
About problem as I mentioned earlier it's working fine with @GET but problem is coming after making it @POST.I already mentioned that It is giving error for first click,if i am clicking it again second time after 1 min it is working fine.In first case webclient call for second microservice URL is not reaching to second micro-service controller.It is giving BAD request for EUREKA URL for second microservices. So I will not able to debug it at second microservice side.
I will try wire logging solution see it works or not
Due to client constraints,I can not share much detail about it.But I am sure it is web client abnormal behaviour. Thanks for your help
Regards,
Manuj Sangwan
Comment From: manuj20
I tried wire logging and found complete stack trace at first microservice end,below is the same:
org.springframework.web.reactive.function.client.WebClientResponseException$BadRequest: 400 Bad Request from POST http://WKWIN93.global.sangwan.net:9898/payment-ag/v1/creditcard/submitCreditCardInfo
at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:179)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 400 from POST http://payment-service/payment-ag/v1/creditcard/submitCreditCardInfo [DefaultWebClient]
Stack trace:
at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:179)
at org.springframework.web.reactive.function.client.DefaultClientResponse.lambda$createException$1(DefaultClientResponse.java:209)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:100)
at com.sgw.common.logging.mdc.MDCContextHelper.onNext(MDCContextHelper.java:30)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1630)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:100)
at com.sgw.common.logging.mdc.MDCContextHelper.onComplete(MDCContextHelper.java:46)
at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onComplete(FluxHide.java:137)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:144)
at reactor.core.publisher.FluxContextStart$ContextStartSubscriber.onComplete(FluxContextStart.java:122)
Actually It seems to me Eureka server creating problem because webclient is not able to wire second microservice in first attempt for this URL but in second attempt it is able to wire with second microservice for same URL. Please suggest us how to proceed now.
Regards, Manuj Sangwn