I need to be able to access the status code of a request. The call can be successful in either of two ways 200 or 201. This is obvious when calling via postman but using the web client, so far I haven't been able to determine which has occurred.
webClient.post()
.uri(url)
.header(HttpHeaders.ACCEPT, MediaType.ALL_VALUE)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.AUTHORIZATION, bearerToken)
.bodyValue(bodyMap)
.retrieve()
.onStatus(
HttpStatus.BAD_REQUEST::equals,
response -> response.bodyToMono(String.class).map(Exception::new))
.bodyToMono(Map.class)
I was thinking maybe I could set an integer variable using within the onStatus() lambda function. Is it even possible to access external variables within a lambda function?
int responseStatus;
// post call
.onStatus(
HttpStatus.CREATED::equals,
response -> ... // do something to set responseStatus
You could use .toEntity(Class<T> bodyClass) method to get entity wrapped in response object ResponseEntity<T>
var response = webClient.post()
.uri(uri)
.retrieve()
.toEntity(Map.class)
.map(res -> {
if (res.getStatusCode().equals(HttpStatus.OK)) {
...
}
return res.getBody();
});
Related
I have a back-end(Springboot) application that is connected to Azure AD and a front-end application that accesses it. In the front-end, I am requiring the user to authenticate using MSAL and passing this authentication to the BE using the On-Behalf-Of flow.
In the front-end, when I am trying to specify the registered client I simple use:
#RegisteredOAuth2AuthorizedClient("back-end") OAuth2AuthorizedClient authorizedClient
I'm trying to create another back-end application that my existing back-end will call and pass the authentication using OBO flow. To check the difference between the initial token from the user and the token the BE will provide to the new BE application, I created a log that fetch the token from these client like authorizedClient.getAccessToken().getTokenValue().
Now that I don't want the explicit approach and want only to add directly in the webclient request the .attributes(clientRegistrationId("new-back-end")), is there any way to check the token? Or at least get the OAuth2AuthorizedClient from the request?
Sample code:
webClient.get()
.uri(new URI(resourceBaseUri + resourceEndpoint))
.attributes(clientRegistrationId("new-be-app"))
.retrieve()
.bodyToMono(String.class)
.block();
• You can do the same as desired by you by using the ‘ServerOAuth2AuthorizedClientExchangeFilterFunction’ to determine the client to use by resolving the ‘OAuth2AuthorizedClient’ from the ‘ClientRequest.attributes()’. The following code shows how to set an ‘OAuth2AuthorizedClient’ as a request attribute: -
#GetMapping("/")
public Mono<String> index(#RegisteredOAuth2AuthorizedClient("okta")
OAuth2AuthorizedClient authorizedClient) {
String resourceUri = ...
return webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient))
.retrieve()
.bodyToMono(String.class)
...
.thenReturn("index");
}
Note: - ‘oauth2AuthorizedClient()’ is a static method in ‘ServerOAuth2AuthorizedClientExchangeFilterFunction’.
Also, please note that the following code shows how to set the ‘ClientRegistration.getRegistrationId()’ as a request attribute: -
#GetMapping("/")
public Mono<String> index() {
String resourceUri = ...
return webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta"))
.retrieve()
.bodyToMono(String.class)
...
.thenReturn("index");
}
You can use the code below also for your purpose: -
#Component
#RequiredArgsConstructor
public class OAuth2Utils {
private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;
public Mono<OAuth2AuthorizedClient> extractOAuth2AuthorizedClient(ServerRequest request) {
return request.principal()
.filter(principal -> principal instanceof OAuth2AuthenticationToken)
.cast(OAuth2AuthenticationToken.class)
.flatMap(auth -> authorizedClientRepository.loadAuthorizedClient(auth.getAuthorizedClientRegistrationId(), auth, request.exchange()));
}
}
Please find the links below for more information: -
How to access OAuth2AuthorizedClient in Webflux Functional Endpoints?
https://docs.spring.io/spring-security/reference/reactive/oauth2/client/authorized-clients.html#_providing_the_authorized_client
I'm able to return ResponseEntity using toEntity() method like below:
#GetMapping("/uri")
public Mono<ResponseEntity<Data[]>> methodName() {
return webClient
.get()
.uri("http://localhost:8088/externalService")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(Data[].class);
}
But I want to process response headers before returning.
The above code converts WebClient response to ResponseEntity and returns immediately but I want to store it in a ResponseEntity variable, process it, and then return the ResponseEntity back.
I referred this -> Spring WebClient Documentation
WHen I tried to store it in a varibale, I get this error -> "block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3"
You can simply use the Reactor's map operator to modify the headers:
return webClient
.get()
.uri("http://localhost:8088/externalService")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(Data[].class)
.map(responseEntity -> responseEntity.getHeaders().add("header", "header-value");
Alternatively, you can use .handle operator in order to provide response processing:
.handle((responseEntity, sink) -> {
if(!isValid(responseEntity)){
sink.error(new InvalidResponseException());
} else if (isOk(responseEntity))
sink.next(responseEntity);
}
else {
//just ignore element
}
})
Spring Starter Web dependency was missing in my pom.xml. Found it and added it back.
Now able to get WebClient response in ResponseEntity format.
I would like to receive the headers (especially the content-type) from the webclient response.
I found this code with flatmap-mono-getHeaders, but it doesn't work.
org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'image/tiff' not supported for bodyType=org.company.MyResponse
How can I fix it? Or maybe can someone recommend a simpler solution.
Mono<Object> mono = webClient.get()
.uri(path)
.acceptCharset(StandardCharsets.UTF_8)
.retrieve()
.toEntity(MyResponse.class)
.flatMap(entity -> Mono.justOrEmpty(entity.getHeaders().getFirst("content-type")));
Object rs = mono.block();
public class MyResponse {
Object body;
Integer status;
String contentType;
}
I would like to receive the headers (especially the content-type) from the webclient response
Generally speaking you can access response headers like this:
ResponseEntity<MyResponse> response = webClient.get()
.uri(path)
.acceptCharset(StandardCharsets.UTF_8)
.retrieve()
.toEntity(MyResponse.class)
.block();
HttpHeaders headers = response.getHeaders();
MediaType contentType = headers.getContentType();
But from the error you pasted it appears that you are accessing an image (image/tiff) that obviously cannot be converted to your MyResponse class.
I'm trying to run this code:
Mono<String> personMono = Mono.just("wfw");
WebClient client = WebClient.create("https://webhook.site");
Mono<Void> result = client.post()
.uri("/a6ad3a35-61c6-4bfc-8d8c-7e5a3e2462aa")
.contentType(MediaType.APPLICATION_JSON)
.body(personMono, String.class)
.exchange()
.flatMap(response -> response.bodyToMono(Void.class));
result.subscribe();
But request is not send. Do you know what I need to add in order to make POST request? I can't find what I'm missing?
I have two interdependent webclient calls to different api's , when the first webclient call response is delayed then readtimeout excpetion is coming . but whenever the first call is success and the second call response is delayed then it is waiting for response indefinitely.
i tried creating seperate instances of webclient for each call . still issue persists.
HttpClient httpClient = HttpClient.create()
.tcpConfiguration(client ->
client.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10))));
return WebClient.builder().baseUrl(url).clientConnector(new ReactorClientHttpConnector(httpClient))
.exchangeStrategies(ExchangeStrategies.withDefaults())
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
.filter(ExchangeFilterFunctions
.basicAuthentication(", "))
.build();
Two subsequent calls are below where , when the second call is delayed response then the readtimeout exception is not thrown
request = request.flatMap(req ->
tempService.getId(loggedInUser, token)
.map(response -> {
req.setRetrieveClientIdentifier(response.getId());
return seRequest;
}))
.zipWhen(request -> tempService.getIdFor(request.getIdentifier(), accountToken)).map(tuple -> {
tuple.getT1().setID(tuple.getT2().getId());
return tuple.getT1();
});