Spring webClient getResponseBodyAsString() is empty(over spring 5.3.13) - spring

I'm using spring webClient for call some Rest API.
try {
webClient.post().uri("apiUrl")
.bodyValue(someParams)
.retrive()
.awaitBody<SomeResponseClass>()
} catch (ex: WebCleintResponseException) {
ex.reponseBodyAsString
}
This is work right on under spring 5.3.12.
I can take response body in ex.
but over spring 5.3.13. is not work.
resposeBody is empty.
webClient.post().uri("apiUrl")
.bodyValue(someParams)
.awaitExchange { response: ClientResponse ->
if (response.satusCode().is2xxSuccessful)
response.awatieBody<SomeResponseClass>
else if (response.statusCodes().isError)
response.awatieBody<SomeResponseClass>
else throw response.createExceptionAndAwaite
}
This working one over spring 5.3.13.
So...What is different from this? I don't find anything on release note.
https://github.com/spring-projects/spring-framework/releases/tag/v5.3.13
I want to know what is changed.
Thanks guys.
Updated
I found this commit on github.
https://github.com/spring-projects/spring-framework/commit/9197f15a306a497b3aa10c6ed48c581b3e938f58

Related

Flux not returning any response SPRING BOOT / MONGO

I am doing the typical chat application with mongodb and spring boot, I declared a tailable cursor and all that stuff, I followed this tutorial https://jskim1991.medium.com/spring-boot-making-a-chat-application-with-webflux-and-mongodb-part-1-5ad09c88f2ce
but the problem is that my controller is not showing any response
Status is 200 but nothing there even if I add another message, I can't get it meanwhile I can see it on the console
this is my code
#GetMapping(value = "/chat/id/{chatId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Chat> getMessages(#PathVariable Integer chatId) {
return chatRepository.findByChatId(chatId).doOnNext(System.out::println)
.subscribeOn(Schedulers.boundedElastic());
}
Does anyone have a clue on what is going on ?

Springboot stream

I have a springboot API which is dealing with lot of processes in backend. I need to stream the status to the frontend. Since I am new to springboot can anyone help me how to achieve this scenario.
Note - Application is going to be containerized in future and I cannot use any cloud service for this.
As there is not much to go off of I will try my best:
If you are using Log4j2 you could simply usethe SocketAppender (external link)
If not:
I did something similar recently and you will need to somehow turn your logs into a stream. I'd advise using the information found here (non-external link)
OutputStream Stream;
#GetMapping("/stream-sse-mvc")
public SseEmitter streamSseMvc() {
SseEmitter emitter = new SseEmitter();
ExecutorService sseMvcExecutor = Executors.newSingleThreadExecutor();
sseMvcExecutor.execute(() -> {
try {
Stream.map(sequence -> SseEmitter.event()
.id(""))
.event("EVENT_TYPE")
.data("String.valueOf(sequence)
.build());
emitter.send(event);
Thread.sleep(1000); //This does not need to be here
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
return emitter;
}
There might be better ways to reach your endpoints but without knowing what Frameworks you are using this is hard to answer. Essentially what we are doing is capturing all log output to a stream which is then broadcasted by an SSE.

Example of error handling calling Restful services with WebFlux

I'm looking for a simple example of error handling with WebFlux. I've read lots of stuff online, but can't find something that fits what I want.
I'm running with Spring Boot 2.45
I am calling services like this:
Mono<ResponseObject> mono = webClient.post()
.uri(url.toString())
.header("Authorization", authToken)
.body(Mono.just(contract), contract.getClass())
.retrieve()
.bodyToMono(ResponseObject.class);
All of my services return Json that is deserialized to ResposeObject which looks something like this:
"success" : true,
"httpStatus": 200,
"messages" : [
"Informational message or, if not 200, then error messages"
],
result: {
"data": {}
}
data is simply a map of objects that are the result of the service call.
If there is an error, obviously success is false.
When I eventually do a ResponseObject response = mono.block(), I want to get a ResponseObject each time, even if there was an error. My service returns a ResponseObject even if it returns an http status of 400, but WebFlux seems to intercept this and throws an exception. Obviously, there might also be 400 and 500 errors where the service wasn't even called. But I still want to wrap whatever message I get into a ResponseObject. How can I eliminate all exceptions and always get a ResponseObject returned?
Update
Just want to clarify that the service itself is not a Reactive Webflux service. It is not returning a Mono. Instead, it is calling out to other Restful services, and I want to do that using Webflux. So what I do is I call the external service, and then this service does a block(). In most cases, I'm calling multiple services, and then I do a Mono.zip and call block() to wait for all of them.
This seems to be what I want to do: Spring Webflux : Webclient : Get body on error, but still can't get it working. Not sure what exchange() is
Correct way of handling this is via .onErrorResume that allows you to subscribe to a fallback publisher using a function, when any error occurs. You can look at the generated exception and return a custom fallback response.
You can do something like this:
Mono<ResponseObject> mono = webClient.post()
.uri(url.toString())
.header("Authorization", authToken)
.bodyValue(contract)
.exchangeToMono(response -> {
if (response.statusCode().equals(HttpStatus.OK)) {
return response.bodyToMono(ResponseObject.class);
}
else if (response.statusCode().is4xxClientError()) {
return response.bodyToMono(ResponseObject.class);
}
else {
Mono<WebClientResponseException> wcre = response.createException();
// examine wcre and create custom ResponseObject
ResponseObject customRO = new ResponseObject();
customRO.setSuccess(false);
customRO.setHttpStatus(response.rawStatusCode());
// you can set more default properties in response here
return Mono.just( customRO );
}
});
Moreover, you should not be using .block() anywhere in your Java code. Just make sure to return a Mono<ResponseObject> from your REST controller. If you want to examine response before returning to client you can do so in a .map() hander like this at the end of pipeline (right after .onErrorResume handler)
.map(response -> {
// examine content of response
// in the end just return it
return response;
});

How to process Observable<Response>

I have one local spring boot application which is hitting another PCF deployed application. The PCF application is giving me list of Student.
I am hitting the same using:
Observable<Response> result = RxObservable.newClient()
.target(url)
.request()
.rx()
.get();
Now I am not able to get my List<Student> back from result Observable.
Tried lots of approaches but nothing really working i.e. subscribing to result etc etc.
So after struggling for few hours below is the solution for now.
Observable observable = result.map(response -> response.readEntity(List.class));
DeferredResult<ResponseEntity<Response>> deferredResult = new DeferredResult(Duration.ofMillis(10000L).toMillis());
observable.subscribe((response) -> {
ResponseEntity<Response> responseData = new ResonseEntity(response, HttpStatus.OK);
deferredResult.setResult(responseData);
}, deferredResult::setErrorResult);
Please suggest, if it can be improved.
Thanks

spring integration dsl http outboundgateway

We are trying to call a REST API using HTTP OutboundGateway using DSL. We are able to make the call using both GET and POST and getting the response as expected. However we couldnt figure a way to pass http headers while making this call using DSL. There are quite a lot of articles about XML approach but couldnt find out documentation with DSL
return IntegrationFlows.from("FileContentChannel")
.handle(Http.outboundGateway("http://host:port/paymentinfo/")
.charset("UTF-8")
.httpMethod(HttpMethod.GET)
.headerMapper(headers)
.expectedResponseType(String.class))
.channel(MessageChannels.queue("APIResponseChannel"))
.get();
We tried with DefaultHttpHeaderMapper as well but it didnt work. Can you please guide us on this?
Thanks to Gary..it worked with this
Update1
return IntegrationFlows.from("FileContentChannel")
.handle(Http.outboundGateway("http://host:port/paymentinfo/")
.charset("UTF-8")
.httpMethod(HttpMethod.GET)
.mappedRequestHeaders("pay*")
.headerMapper(headerMapper())
.expectedResponseType(String.class))
.channel(MessageChannels.queue("APIResponseChannel"))
.get();
#Bean
HeaderMapper headerMapper() {
DefaultHttpHeaderMapper headerMapper = new DefaultHttpHeaderMapper();
String[] headerNames = {"payment-hdr1","payment-hdr2"};
headerMapper.setOutboundHeaderNames(headerNames);
headerMapper.setUserDefinedHeaderPrefix("");
return headerMapper;
}
return IntegrationFlows.from("FileContentChannel")
.enrichHeaders(h -> h.header("foo1", "bar")
.header("foo2", "baz"))
.handle(Http.outboundGateway("http://host:port/paymentinfo/")
.charset("UTF-8")
.httpMethod(HttpMethod.GET)
.mappedRequestHeaders("foo*")
.expectedResponseType(String.class))
.channel(MessageChannels.queue("APIResponseChannel"))
.get();
Custom headers will (currently) get an X- prefix.
To avoid that use a customized DefaultHeaderMapper to map the required header patterns and use a userDefinedHeaderPrefix of "".

Resources