Getting Response from Spring Web Client onStatus function - spring

WebClient
.builder()
.build()
.post()
.uri("/some-resource")
.retrieve()
.onStatus(
HttpStatus.INTERNAL_SERVER_ERROR::equals,
response -> response.bodyToMono(String.class).map(Exception::new))
I'm trying to write some test cases for a function like this in a project that I'm working on. I can't figure out how do I access the response in the onStatus parameters, or how to pass a specific HTTP status code to this onStatus function to be able to simulate the response.
I don't really understand where the onStatus function is getting the HTTP status from, or where it is passing the response to. Am I missing something here?

Related

VertX HTTP GraphQL request responds with 'Query is missing'

When attempting a HTTP request message to the Vert.X HTTP server endpoint, it fails with the error 'io.vertx.core.impl.NoStackTraceThrowable: Query is missing'.
This happens when the Query is passed as part of the request body. The same is NOT observed when the 'query' is provided in the URL.
Adding the following code did not help either.
router.route().handler(BodyHandler.create());

Spring WebClient POST and Content Length Header for application/x-www-form-urlencoded

I am using Spring WebClient and Spring Boot 2.3.5.RELEASE to POST a request to a site that wants a Content Type of application/x-www-form-urlencoded. It keeps failing because the Content-Length header is not set. I can set that in the code, but I'm not sure how to compute the Content-Length when the Content-Type is application/x-www-form-urlencoded. The URL I am accessing is a legacy site. I saw this post Missing Content-Length header sending POST request with WebClient (SpringBoot 2.0.2.RELEASE) but it doesn't address the issue for Content-Type=application/x-www-form-urlencoded.
I executed the same request in Postman, and it works fine. In Postman if I remove the Content-Length header I get the same error I see in the code.
Please advise how to compute the Content-Length. Thank you.
Here is a snippet.
final MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("key 1, "value 1");
formData.add("key 2, "value 2");
formData.add("key 3, "value 3");
formData.add("key 4, "value 4");
ResponseEntity<String> resp = webClient
.post()
.uri("https://myurl")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.header(HttpHeaders.CONTENT_LENGTH,
String.valueOf(???))
.body(BodyInserters.fromFormData(formData))
.exchange()
.flatMap(response -> response.toEntity(String.class))
.block();
I have a solution. Turns out that this had nothing to do with Content-Length. I decided to try accessing the same REST endpoint with RestTemplate. I got an identical error where the target endpoint complained about missing parameters. Yet, when I accessed it with Postman or curl it worked perfectly fine.
Instead of using BodyInserters.fromFormData with a MultiValueMap, I used BodyInserters.fromValue(bodyData). For the body data I built a String of values similar to what curl used.
grant_type=client_credentials&client_id=123&client_secret=456&scope=myScope
This issue is perhaps limited to the host serving this API(not a public one), but I found it interesting that RestTemplate and WebClient both failed with a standard approach for x-www-form-urlencoded, while Postman and curl worked just fine.

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.

How to log Spring WebClient response

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.

Cannot put parameters in body for OAuth2 POST requests in a REST service

It seems I am missing something very basic here.
I made a REST Api that takes POST requests for generating tokens using the Apache Oltu OAuth2 service, that looks something like this :
#POST
#Consumes("application/x-www-form-urlencoded")
#Produces("application/json")
public Response authorize(#Context HttpServletRequest request) throws OAuthSystemException, IOException {
try {
OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
When I use HttpRequester or Postman to test the service, it works perfectly fine on condition that I input all authentication and OAuth2 parameters as input parameters, as an example :
https://localhost:8443/rest/OAuthService/token?grant_type=password&username=userfortest&password=Johhny1é&client_id=1234
However I read, that for any POST requests, all parameters should be
in the Body of the HTTP request and never sent through with the url as a simple parameter. When I try to pass it in the body of the HTTP request, so as to make the request secure (so the url is the same without parameters and all params are specified in the body), it seems like it doesn't receive anything from the body as it throws an exception, after
OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
with the following message :
{"error_description":"Missing grant_type parameter value","error":"invalid_request"}
Is it the intended behaviour of Oltu/OAuth2 for the parameters to be passed through with the url? Or what am I doing wrong?
Thanks in advance.
Your answer is here: Unable to retrive post data using ,#Context HttpServletRequest when passed to OAuthTokenRequest using Oltu
I did exactly what he said and it worked perfectly.
You need modify Response authorize() parameters.

Resources