RestTemplate seems to keep Authorization header - java

I'm using Spring 4 RestTemplate to do some server-side API calls.
The RestTemplate instance is a custom one (not Spring Boot default) using Apache HttpClient created as follows:
PoolingHttpClientConnectionManager cm;
...
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
Some of the API calls use HTTP Basic Authentication and thus need to have an Authorization header. I'm adding this on a RequestEntity and then performing an exchange() call on the RestTemplate and this works fine.
RequestEntity<Void> requestEntity;
requestEntity = RequestEntity.get(uri)
.accept(MediaType.valueOf("application/repository.folder+json"))
.acceptCharset(UTF_8)
.header("Accept-Encoding", "")
.header("Authorization", apiBasicAuthHeader())
.build();
Some other API calls (to the same backend server) should not use HTTP Basic Authentication and instead use a pre-authenticated token supplied as a request parameter.
RequestEntity<Void> requestEntity = RequestEntity.get(uriWithToken)
.accept(APPLICATION_JSON)
.acceptCharset(UTF_8)
.header("Accept-Encoding", "")
.build();
operations.exchange(requestEntity, ResourceLookupResults.class)
This also works fine by itself.
However, if I do an API call using the Authorization header first and then try to do one with the pre-authenticated token (with the same RestTemplate), it seems that the Authorization header is still sent on the 2nd request. I'd expect the header added to the RequestEntity to be added only for that specific request and not for subsequent requests that don't need it. Why is this and what is the best way to avoid this (like using separate RestTemplate instances)?

Related

How to handle HTTP 204 No Content Response in Spring

From my spring boot app I am making multiple calls to an API search service using the Spring Web Client. This is required due to pagination and multiple search params that cannot be used together.
When making calls with certain params I am getting HTTP 204 No Content, which is completely normal and expected. However this is causing an issue with decoding the body to my Response object
I am attempting to handle the 204 status in a filter but what I am doing seems a bit wonky and wondering how this should be handled. I am new to the reactive style but want to avoid using the deprecated RestTemplate style.
.builder()
.filter(WebClientFilter.handleError())
.filter(responseFilter)
.clientConnector(new ReactorClientHttpConnector(HttpClient.create().followRedirect(true)))
... default header stuff ommitted ...
.build().post().uri(searchServiceUrl)
.body(BodyInserters.fromValue(createsearchRequest()))
.retrieve()
.bodyToMono(SearchResponse.class)
.block();
Here is where I am filtering for the 204 and returning a newly constructed Response with my empty Dto. This just seems wrong that I am replacing the response from the server with my own, but if I do not do this the WebClient returns null causing other issues.
private static Mono<ClientResponse> exchangeFilterResponseProcessor(ClientResponse response) {
HttpStatus status = response.statusCode();
if (HttpStatus.NO_CONTENT.equals(status)) {
return response.bodyToMono(String.class).flatMap(body -> {
log.info("Body is {}" , body);
ClientResponse emptyResponse = ClientResponse.create(HttpStatus.OK)
.header(CONTENT_TYPE, "application/json")
.body(new SearchResponse().toString())
.build();
return Mono.just(emptyResponse);
});
}
return Mono.just(response);
}
Should I refactor the code to just allow the null response and deal with it that way vs trying to do it in the code above?

Calling multiple external APIs in Spring Boot

I am working on a project but it requires me to call multiple external APIs. I basically have to call an API to get a player id by giving a name. Then use that player id to get a list of match ids. Then make calls for each match id to get details on each match. its alot and doesnt seem optimal but its the only way to do it. I was going to use rest template to make a call to the following
https://americas.api.riotgames.com/lol/match/v5/matches/by-puuid/HDzjdaStxhHcceGGd8qJcc4Vw45FOlOQ1PNXKQ0h9_iqfwHP3oI0spl1bLUOw_7_J49vzaIKylv5Vg/ids?start=0&count=20
I have to pass in headers as well such as
riot token : token
"Origin": "https://developer.riotgames.com"
I was wondering how I can do this in Java Spring boot. I saw RestTemplate would be used but I couldnt figure out how to include the headers. Any guidance would be appreciated.
You can call RestTemplate.exchange() using either the method signature with RequestEntity or with HttpEntity.
// Using RequestEntity
RequestEntity<?> request= RequestEntity.get(url).header(headerName, headerValue).build();
ResponseEntity<String> response = restTemplate.exchange(request, String.class);
// Using HttpEntity
HttpHeaders headers = new HttpHeaders();
headers.set(headerName, headerValue);
HttpEntity httpEntity = new HttpEntity(/* this is nullable */ requestBody, headers);
ResponseEntity,String> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
Would also recommend reading through the following:
https://howtodoinjava.com/spring-boot2/resttemplate/spring-restful-client-resttemplate-example/
https://www.baeldung.com/rest-template
How to set an "Accept:" header on Spring RestTemplate request?

rest api - how to send body parameters of postman in rest api call

Hi I am able to call a rest api through postman . I have added the following in body part of postman. client_id - xxxx , client_secret - *** etc.
Now i want to make rest api call through java. How do i add these body parameters to request being created from java
If you are using Spring, you can use the RestTemplate Spring REST client to make a call to an external API from Java.
Example:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>(someDto, headers)
restTemplate.postForEntity(postUrl, request, String.class);

Using Spring Boot 2 WebClient, in threadsafe / per-request manner, how send diff headers every request?

In Spring Boot 1.5.x, I could use interceptors with AsyncRestTemplate to grab headers from an incoming request to a RestController endpoint and put them in any exchange requests made via the AsyncRestTemplate.
I don't see how this can work with the WebClient. It looks like if you build a WebClient that all its headers, etc are set and unchangeable:
WebClient client = WebClient.builder()
.baseUrl( "http://blah.com" )
.defaultHeader( "Authorization", "Bearer ey..." )
.build();
While I can change these using client.mutate(), that instantiates a completely new WebClient object. I'd prefer not to have to create a new one on every request. Is there no way to keep a WebClient and have per-request headers and other parameters?
It seems like a big waste and poor performance to force creating a new object every time.
What you're using here are the default headers that should be sent for all requests sent by this WebClient instance. So this is useful for general purpose headers.
You can of course change the request headers on a per-request basis like this:
Mono<String> result = this.webClient.get()
.uri("/greeting")
.header("Something", "value")
.retrieve().bodyToMono(String.class);
If you wish to have an interceptor-like mechanism to mutate the request before sending it, you can configure the WebClient instance with a filter:
WebClient
.builder()
.filter((request, next) -> {
// you can mutate the request before sending it
ClientRequest newRequest = ClientRequest.from(request)
.header("Something", "value").build();
return next.exchange(newRequest);
})
Please check out the Spring Framework documentation about WebClient.

Basic authentication once time for all

I developped a spring-boot application. When I need to do REST requests (POST, PUT...) I use every time basic authentication like that
HttpEntity<PostJql> httpEntity = new HttpEntity<PostJql>(post, jiraUtils.getHeader());
ResponseEntity<IssueDataWrapper> response = restTemplate.exchange(urlJiraResponse, HttpMethod.POST, httpEntity, IssueDataWrapper.class);
Is there another way to do basic authentication once time in the application and then use RestTemplate or httpPost {Put/Delete...} without basic authentication ?
Best regards
Yes. Configure your RestTemplate with a ClientHttpRequestFactory that does what you need.
You can just set it in the constructor:
restTemplate = new RestTemplate(requestFactory);
... or you can make all of these things beans and let Spring wire it.
You could extend SimpleClientHttpRequestFactory, overriding createRequest():
public class BasicAuthSimpleClientHttpRequestFactory
extends SimpleClientHttpRequestFactory {
#Override
public HttpClientRequest createRequest(URI uri, HttpMethod httpMethod) {
HttpClientRequest request = super.createRequest(uri, httpMethod);
HttpHeaders headers = request.getHeaders();
headers.add(... your auth header ...);
return request;
}
}
... or you could bring in Apache HttpComponents as a dependency and use an HttpComponentsClientHttpRequestFactory, and configure the underlying Apache HttpClient to do the authentication you need. Apache documents this in detail, but to get you started:
BasicCredentialsProvider credentialsProvider =
new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("user", "password"));
HttpClient client = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credentialsProvider)
.build();
RestTemplate restTemplate = new RestTemplate(
new HttpComponentsClientHttpRequestFactory(client));
In my experience it's worth bringing in Apache HttpComponents if you can -- it is more reliable and configurable than the HttpUrlConnection based HTTP client that RestTemplate otherwise uses by default. When your requirements broaden to things like other authentication methods, timeouts, retry strategies etc., you'll be glad you have an HttpClient to configure.

Categories

Resources