How to log Spring WebClient response - spring

I'm new to Spring WebClient. Can someone advise the best way to log REST request and response from another webservice?
I've already seen an example of logging request within the question but also have to log a response and a request for a POST call.
how to log Spring 5 WebClient call
Thank you.

One option is to use the onStatus function. The advantage is that you can react differently on different status codes:
.onStatus(HttpStatus::is4xxClientError, res -> {
res.toEntity(String.class).subscribe(
entity -> log.warn("Client error {}", entity)
);
return Mono.error(new HttpClientErrorException(res.statusCode()));}
)
But be aware that this will log asynchronously, that means it might log after you already logged something different. I'm using this way right now but I know it is not perfect, so I will be happy to see better suggestions.

Related

HTTP Status to return if resources are partially created in Spring boot - rest api?

I've a scenario where a post request from first microservice creates new user and creates a wallet for newly created user from different microservice.
I'm returning HTTP status 201 when both user and wallet created. I'm bit confused what status should I return if user is created but wallet isn't.
I came across some articles and found two relevant to my confusion but are contradictory on returning HTTP status 207 due to its limitation related to WebDAV.
Is Http response 207 MULTI-STATUS appropriate for multi task operations?
REST API response in partial success
refer my code -
#PostMapping("/register")
public ResponseEntity<User> saveUser(#RequestBody User user) {
user.setUserId(sequenceGeneratorService.generateSequence(User.SEQUENCE_NAME));
user.getRoles().forEach(role -> role.setRoleId(sequenceGeneratorService.generateSequence(Role.SEQUENCE_NAME)));
User savedUser = userService.saveUser(user);
ResponseEntity<Wallet> createdWallet = createUserWallet(savedUser);
if (createdWallet.getStatusCode().is2xxSuccessful()) {
savedUser.setWallet(createdWallet.getBody());
return new ResponseEntity<User>(savedUser, HttpStatus.CREATED);
} else {// here is the confusion
return new ResponseEntity<User>(savedUser, HttpStatus.MULTI_STATUS);
}
}
private ResponseEntity<Wallet> createUserWallet(User savedUser) {
Wallet userWallet = Wallet.builder()
.walletId(sequenceGeneratorService.generateSequence(Wallet.SEQUENCE_NAME))
.userId(savedUser.getUserId())
.balance(BigDecimal.ZERO).build();
return walletServiceProxy.createWallet(userWallet);
}
May I know which status should I return here?
I'm returning HTTP status 201 when both user and wallet created. I'm bit confused what status should I return if user is created but wallet isn't.
HTTP status codes are metadata of the transfer-of-documents-over-a-network domain (see Webber 2011); they are there so that general purpose HTTP components can correctly interpret the response, and do intelligent things (like invalidating previously cached responses, when appropriate).
Your HTTP API is a facade: a costume that your implementation wears that makes it look like an HTTP aware document store. (The fact that your implementation doesn't have "documents" is an implementation detail, hidden behind this facade.)
The responses you send should be understood in this same way - you are telling the HTTP client (and also any intermediary components who can read the response metadata) how your (imaginary) web page reacted to the request that was sent.
Was the message processed successfully? Did it create a new (imaginary) web page with its own identifier, that clients can send messages to? Then you should normally be sending back a 201 Created, even if the implementation didn't achieve that outcome via the "happy path".
On the other hand, if you want general purpose components to understand that request processing failed, you send a 4XX Client Error or a 5XX Server Error, as appropriate.
(You probably shouldn't be using 207 MultiStatus unless you are deliberately doing WebDav things and are expecting requests from WebDav aware components; it doesn't achieve anything useful unless the client implementation knows how to handle multistatus XML documents).
Reminder: the part of an HTTP response where you describe in detail what happened and how the end consumer can respond to it is the body of the HTTP response.
201 Created
Location: /users/12345
Content-Type: text/plain
Hey, we created the new user you asked us to. Isn't that great?
You can review the details of the user at: /users/12345
But, we weren't able to create a wallet for the user. If that's
kind of important to you, could you fill in this form and send it
to us: /lost-wallets#form ?
Thanks, and have a great day

Why Rest End point is not showing any json data while using POST method in POSTMAN application?

I am trying to implement sample spring boot project and to ensure my endpoints are working properly, i'm using POSTMAN. When using POSTMAN , I am not able to see the response(i.e in Pretty) for a POST request. But the Status is 200 OK and I am able to see the result using GET request.
No Pretty response for POST request
GET Response ensuring that the previous POST request works fine
And my controller code is the following
#PostMapping("/message")
public Message createMessage(#RequestBody Message message)
{
return service.createMessage(message);
}
Can anyone help me to find out why I am not able to see the result while using POST method please?
Like Rafael says it is good to return a Response with the object entity. I haven't been working with Spring myself but with JavaEE and in JavaEE it is perfectly possible to return the object directly without using a Response. I use Responses anyways though, because it is much nicer to work with, and you can create your own custom responses and status codes.
Maybe check if your createUser service actually returns a message.
I don't know much about Spring, but usually what works for me is using a ResponseEntity as the object returned by the function. Also, maybe you should use #RestController as the annotation to your class controller
#PostMapping("/message")
public ResponseEntity<Message> createMessage(#RequestBody Message message)
{
Message msg = service.createMessage(message);
return ResponseEntity.ok(msg);
}

Spring boot Webclient's retrieve vs exchange

I have started using WebClient in my Spring boot project recently.
Can somebody throw some light on the differences/usages between exchange and retrieve method in WebClient.
I undertand that exchange returns Mono<ClientResponse> and retrieve returns ResponseSpec, I just want to know when/why I should use each one of them.
Much Thanks.
Adding to #JArgente's answer.
According to the official documentation of the retrieve() method:
Perform the HTTP request and retrieve the response body.
...
This method is a shortcut to using exchange() and decoding the response body through
ClientResponse.
and the exchange() method
Perform the HTTP request and return a ClientResponse with the response status and headers. You can then use methods of the response to consume the body:
The retrieve() method decodes the ClientResponse object and hands you the ready-made object for your use. It doesn't have a very nice api for handling exceptions.
However on the other hand the exchange() method hands you the ClientResponse object itself along with the response status and headers. With exchange method you get fine grained control over your response objects and a better way to handle the response object and the exceptions.
If you just want to consume some api go with retrieve().
If you want a better control over your response objects, headers and exceptions, go with exchange().
Update 1
Starting from Spring 5.3, the exchange() method is deprecated due to possible memory/connection leaks. exchangeToMono() or exchangeToFlux() can be used instead.
Thanks #rhubarb for the update.
According to spring Webclient api documentation the difference between the two is that exchange retrieve in addition to the body other http response information like headers and status, while retrieve only returns body information.
So If you only need the body information you should use retrieve, because it is a shortcut for exchange and then get the body, but if you need other information like http status you must use exchange.

Spring PostMapping return 401 without body

I want to make a Post to write some data into the database, but all needed information is stored on the server, so my Post service requires no body:
#PostMapping("foo")
public #ResponseBody
RestResponse writeFoo() {
// WRITE AND RETURN
}
If I try to make a post request to this service I receive 401 even if I pass a valid token. If I change my exposed service to a GetMapping all works as expected. It seems that I can't manage a Post request with an empty body.
I've tried adding some fake parameters as
RestResponse writeFoo(#RequestBody(required = false) String fake)
but without success.
Any idea?
The issue you explain is most commonly the cause of bad (or missing?) configuration.
Pay attention that i.e. GET method is allowed by default by your REST API, while you need to specify other method types (i.e. PUT and POST), otherwise it won't work out of the box due to CORS.
The part where GET method works while POST method doesn't is a strong hint towards missing/incorrect CORS configuration. You can fix it quickly by adding some CORS filter and setup your response headers.
The official documentation should give you a good start, if you don't know where to look for: Spring docs - enabling CORS
UPDATE:
The issue is successfully resolved, check comments section for more info.
Short story - back-end configuration for CORS/CSRF token was set up correctly in this particular case, the issue occurred due to missing header (CSRF token) on the angular/front-end part of the webapp.

Prevent client repeatedly sending requests

I have a spring controller and request mapped method.
#RequestMapping(value = { "/doSomething.do" })
public ModelAndView emailToNonBelievers(){
.....
// do something which takes lot of time
// send response..
return modelAndView;
}
This method takes very long time, about an hour.(It is for Admin, not users. And Admin doesn't need to wait an hour. Just fire and forget, that's ok. And this is not a batch job)
But I found that client send requests repeatedly with 3 minutes interval(observed 6 times and I stopeed Spring service).
I guess because client didn't get any response from server.
If my guess is right, server should response sort of "Your request is accepted, just shut up and wait!!" .
But how to send response(200 ok) before jobs finished in Spring?
Or am I missing something?
In this situation it would be recommended to use asynchronous task processing. Spring comes with out-of-box support for it via #Async annotation.Consult my answer for detailed setup for similar query and here for docs.

Resources