Spring WebClient async callback not called when http server response 404 - spring

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());

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.

(VueJS, Axios) Different way to catch errors

I'm currently building a single page application based on Laravel and VueJS.
Is there any better way then mine to handle errors with axios?
This is how I currently do it when a user clicks on login button:
VueTemplae:
methods : {
authenticateUser() {
axios.post('/api/login', this.form).then(() => {
this.$router.push({name : 'home'});
}).catch((error) => {
this.error = error.response.data.message;
});
}
}
Api route:
public function login() {
try {
// do validation
} catch(Exception) {
// validation failed
throw new Exception('login.failed');
}
// manually authentication
if(Auth::attempt(request()->only('email', 'password'))) {
return response()->json(Auth::user(), 200);
}
// something else went wrong
throw new Exception('login.failed');
}
Unfortunately, throwing an exception always prints an internal server error into the console.
If I return something else than an exception, axios always executes then().
Is there any way to prevent this or a better way to handle axios responses?
Thank you!
Your API needs to return a response with a 4XX status code in order for the catch block to fire in your Vue component.
Example:
After you catch the error on the API side, send a response with status code 400 Bad Request. It will be formatted similarly to your successful login response, but with an error message and 400 status code instead of 200.

How to return instance of Error using Spring REST call

In my front end application, I have to handle the error response of the HTTP REST call.
Front end:
restservice.check().subscribe(
response => {
if (response != null) {
},
error => {
if (error instanceof Error) {
}}
});
Controller.java
public ResponseEntity updateEstablishment
{
return new ResponseEntity<>(obj, HttpStatus.OK);
}
How can I return an instance of Error here.
REST services should catch and handle internal exception. And should return meaningful error code and message back to client. Please find below link to best way to handle exception in REST
https://www.baeldung.com/rest-api-error-handling-best-practices

Sending appropriate error responses on web actions

I have some web-enabled actions that are exposed through API Connect in IBM Cloud Serverless Functions.
Some of my actions use request-promises to call external REST services and I need to be able to catch an error and respond with an appropriate status-code to the caller.
Since the actions are web-enabled, the documentation indicates that I can use an annotated JSON to set the headers, status-code and body of the response. But it seems that, seems the API expects to always get a Content-Type=application/json, the response processor is failing to understand my annotations in the case of an error.
I tried the following without success:
let rp = require('request-promise');
function main(params){
//setup options
return rp(options).then(
res => {
return res;
}
).catch(
err => {
return { error: { statusCode:err.statusCode } }
}
);
}
Another variation:
let rp = require('request-promise');
function main(params){
//setup options
return rp(options).then(
res => {
return res;
}
).catch(
err => {
return { statusCode:err.statusCode }
}
);
}
The problem is that the status-code I always get is 200... I also tried to change the runtime to node8.0 without success.
Thanks!
I found the answer myself :)
In order to get the status-code and headers, one must set the field Response Content Type to `Use "Content-Type" header from action", while setting up the mapping between the API call and the action....

Resources