Sending GET request with Authentication headers using restTemplate - java

I need to retrieve resources from my server by sending a GET request with some Authorization headers using RestTemplate.
After going over the docs I noticed that none of the GET methods accepts headers as a parameter, and the only way to send Headers such as accept and Authorization is by using the exchange method.
Since it is a very basic action I am wondering if I am missing something and there another, easier way to do it?

You're not missing anything. RestTemplate#exchange(..) is the appropriate method to use to set request headers.
Here's an example (with POST, but just change that to GET and use the entity you want).
Here's another example.
Note that with a GET, your request entity doesn't have to contain anything (unless your API expects it, but that would go against the HTTP spec). It can be an empty String.

You can use postForObject with an HttpEntity. It would look like this:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);
HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
String result = restTemplate.postForObject(url, entity, String.class);
In a GET request, you'd usually not send a body (it's allowed, but it doesn't serve any purpose). The way to add headers without wiring the RestTemplate differently is to use the exchange or execute methods directly. The get shorthands don't support header modification.
The asymmetry is a bit weird on a first glance, perhaps this is going to be fixed in future versions of Spring.

Here's a super-simple example with basic authentication, headers, and exception handling...
private HttpHeaders createHttpHeaders(String user, String password)
{
String notEncoded = user + ":" + password;
String encodedAuth = "Basic " + Base64.getEncoder().encodeToString(notEncoded.getBytes());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Authorization", encodedAuth);
return headers;
}
private void doYourThing()
{
String theUrl = "http://blah.blah.com:8080/rest/api/blah";
RestTemplate restTemplate = new RestTemplate();
try {
HttpHeaders headers = createHttpHeaders("fred","1234");
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = restTemplate.exchange(theUrl, HttpMethod.GET, entity, String.class);
System.out.println("Result - status ("+ response.getStatusCode() + ") has body: " + response.hasBody());
}
catch (Exception eek) {
System.out.println("** Exception: "+ eek.getMessage());
}
}

These days something like the following will suffice:
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
restTemplate.exchange(RequestEntity.get(new URI(url)).headers(headers).build(), returnType);

All of these answers appear to be incomplete and/or kludges. Looking at the RestTemplate interface, it sure looks like it is intended to have a ClientHttpRequestFactory injected into it, and then that requestFactory will be used to create the request, including any customizations of headers, body, and request params.
You either need a universal ClientHttpRequestFactory to inject into a single shared RestTemplate or else you need to get a new template instance via new RestTemplate(myHttpRequestFactory).
Unfortunately, it looks somewhat non-trivial to create such a factory, even when you just want to set a single Authorization header, which is pretty frustrating considering what a common requirement that likely is, but at least it allows easy use if, for example, your Authorization header can be created from data contained in a Spring-Security Authorization object, then you can create a factory that sets the outgoing AuthorizationHeader on every request by doing SecurityContextHolder.getContext().getAuthorization() and then populating the header, with null checks as appropriate. Now all outbound rest calls made with that RestTemplate will have the correct Authorization header.
Without more emphasis placed on the HttpClientFactory mechanism, providing simple-to-overload base classes for common cases like adding a single header to requests, most of the nice convenience methods of RestTemplate end up being a waste of time, since they can only rarely be used.
I'd like to see something simple like this made available
#Configuration
public class MyConfig {
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate(new AbstractHeaderRewritingHttpClientFactory() {
#Override
public HttpHeaders modifyHeaders(HttpHeaders headers) {
headers.addHeader("Authorization", computeAuthString());
return headers;
}
public String computeAuthString() {
// do something better than this, but you get the idea
return SecurityContextHolder.getContext().getAuthorization().getCredential();
}
});
}
}
At the moment, the interface of the available ClientHttpRequestFactory's are harder to interact with than that. Even better would be an abstract wrapper for existing factory implementations which makes them look like a simpler object like AbstractHeaderRewritingRequestFactory for the purposes of replacing just that one piece of functionality. Right now, they are very general purpose such that even writing those wrappers is a complex piece of research.

A simple solution would be to configure static http headers needed for all calls in the bean configuration of the RestTemplate:
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate getRestTemplate(#Value("${did-service.bearer-token}") String bearerToken) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add((request, body, clientHttpRequestExecution) -> {
HttpHeaders headers = request.getHeaders();
if (!headers.containsKey("Authorization")) {
String token = bearerToken.toLowerCase().startsWith("bearer") ? bearerToken : "Bearer " + bearerToken;
request.getHeaders().add("Authorization", token);
}
return clientHttpRequestExecution.execute(request, body);
});
return restTemplate;
}
}

Related

Is there a way to get the bearerToken set in HttpHeaders to perform validation in test?

I have a method that builds HttpHeaders. It fetches a bearerToken after logging in and sets it in the header. I would like to verify that the token is being set in a unit test, but I am unable to find any method to get the token from the header.
This is the method that builds the headers:
public static HttpHeaders buildHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
String bearerToken = getToken();
headers.setBearerAuth(bearerToken);
return headers;
}
And this is my test:
#Test
void shouldBuildHeaders() {
HttpHeaders httpHeaders = Util.buildHeaders();
assertNotNull(httpHeaders);
assertEquals(httpHeaders.getAccept(), Collections.singletonList(MediaType.APPLICATION_JSON));
//I would like to do something like this to verify if the token is being set.
// String bearerToken = httpHeaders.getBearerToken();
// assertNotNull(bearerToken);
}
Is there any way to get the token, or is there anything wrong in the way I am testing?
You can try to loop trough your headers to see the keys that are available, like this:
httpHeaders.forEach((key,value)->{
System.out.println(key+"-->"+value);
});
This allows you to check on all the headers that you have.
However in you case you can just look up the specific header that you want, by looking at the documentation bellow (I'm assuming you are using Spring):
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpHeaders.html#setBearerAuth-java.lang.String-
The header key is actually "Authorization", meaning that you can access it's value by using the code bellow:
String bearerToken = httpHeaders.getFirst(HttpHeaders.AUTHORIZATION);

Sending request body in GET method not working

Im currently in a task in which i have to send a GET request with a body. Im a aware this isn't a good practice and that i should send the json through query params.
But I'm bound to do it like this.
So let's continue. I use RestTemplate with exchange but due to SimpleClientHttpRequestFactory implementation i cannot send a body with a GET method.
RestTemplate template = new RestTemplate(new CustomClientHttpRequestFactory());
httpHeaders.setContentType(APPLICATION_JSON);
httpHeaders.set("token", token.getToken());
httpHeaders.set("companyId", companyId);
URI uri = new URI(getInspectionsUrl);
HttpEntity<InspectionsInputDTO> entity = new HttpEntity<InspectionsInputDTO>(inputDTO, httpHeaders);
response = template.exchange(uri, GET, entity, InspectionsResponseDTO.class);
After some research i found the following code:
class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if ("GET".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
// RestTemplate initialization
RestTemplate template = new RestTemplate(new CustomClientHttpRequestFactory());
This tries to override SimpleClientHttpRequestFactory httpMethod allowance but id does not work. The question is, how can i send a Request BODY in GET request with RestTemplate. Maybe there is another way to override SimpleClientHttpRequestFactory. Im new in this strange world of spring, sorry if im saying something wrong (:

unable to post large files using spring rest template w/ custom interceptor

I am trying to POST a large file from one microservice to another using spring rest template POST w/ custom interceptor as follows:
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate = new RestTemplate(requestFactory);
restTemplate.getInterceptors().add({customInterceptor});
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", {InputStreamResource});
body.add("metadata", {JSON string});
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
restTemplate.exchange({url}, HttpMethod.POST, requestEntity, ...);
(It makes no difference whether I use a SimpleClientHttpRequestFactory or HttpComponentsClientHttpRequestFactory)
Adding an interceptor results in the creation of an new InterceptingClientHttpRequestFactory (which wraps the original request factory) on a call to getRequestFactory.
This works fine for smaller files but for large files - since requests are never delegated to the original request factory, no streaming ever occurs and, hence, results in a java.lang.OutOfMemoryError: Java heap space exception.
Any help would be appreciated.
It seems this problem of RestTemplate will not be fixed according to this issue
we're not going to support this, now that the WebClient is available and provides first-class support for streaming.
Note that WebClient is available in Spring 5.
I have also come across this issue, but switching to WebClient is not an option at this point in time so I opted for a work around by implementing a new ClientHttpRequestFactory (albeit via an AbstractClientHttpRequestFactoryWrapper); it mainly works due to the fact that we are only tweaking the headers and not the body.
SimpleClientHttpRequestFactory simpleRequestFactory =
new SimpleClientHttpRequestFactory();
simpleRequestFactory.setConnectTimeout(10000);
simpleRequestFactory.setReadTimeout(60000);
simpleRequestFactory.setBufferRequestBody(false); // this enables streaming
SomeHeaderInterceptingClientHttpRequestFactory interceptingRequestFactory =
new SomeHeaderInterceptingClientHttpRequestFactory(simpleRequestFactory);
RestTemplate restTemplate = new RestTemplate(interceptingRequestFactory);
and this is what the SomeHeaderInterceptingClientHttpRequestFactory looks like:
public class SomeHeaderInterceptingClientHttpRequestFactory
extends AbstractClientHttpRequestFactoryWrapper {
// you can have fields here that are initialized in the constructor
// e.g. a service that supplies the header value that you want to populate
public SomeHeaderInterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
// initialize fields
}
#Override
protected ClientHttpRequest createRequest(URI uri,
HttpMethod httpMethod,
ClientHttpRequestFactory requestFactory)
throws IOException {
ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
HttpHeaders headers = request.getHeaders();
headers.set("SOME_HEADER", "some value");
return request;
}
}
If you want to do something to the body then you could also try to implement a new ClientHttpRequest. You will then return a new instance of your newly introduced ClientHttpRequest in the createRequest method of your newly introduced ClientHttpRequestFactory.

How to extract HTTP status code from the RestTemplate call to a URL?

I am using RestTemplate to make an HTTP call to our service which returns a simple JSON response. I don't need to parse that JSON at all. I just need to return whatever I am getting back from that service.
So I am mapping that to String.class and returning the actual JSON response as a string.
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
return response;
Now the question is -
I am trying to extract HTTP Status codes after hitting the URL. How can I extract HTTP Status code from the above code? Do I need to make any change into that in the way I doing it currently?
Update:-
This is what I have tried and I am able to get the response back and status code as well. But do I always need to set HttpHeaders and Entity object like below I am doing it?
RestTemplate restTemplate = new RestTemplate();
//and do I need this JSON media type for my use case?
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
//set my entity
HttpEntity<Object> entity = new HttpEntity<Object>(headers);
ResponseEntity<String> out = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
System.out.println(out.getBody());
System.out.println(out.getStatusCode());
Couple of question - Do I need to have MediaType.APPLICATION_JSON as I am just making a call to url which returns a response back, it can return either JSON or XML or simple string.
Use the RestTemplate#exchange(..) methods that return a ResponseEntity. This gives you access to the status line and headers (and the body obviously).
getStatusCode()
getHeaders()
If you don´t want to leave the nice abstraction around RestTemplate.get/postForObject... methods behind like me and dislike to fiddle around with the boilerplate stuff needed when using RestTemplate.exchange... (Request- and ResponseEntity, HttpHeaders, etc), there´s another option to gain access to the HttpStatus codes.
Just surround the usual RestTemplate.get/postForObject... with a try/catch for org.springframework.web.client.HttpClientErrorException and org.springframework.web.client.HttpServerErrorException, like in this example:
try {
return restTemplate.postForObject("http://your.url.here", "YourRequestObjectForPostBodyHere", YourResponse.class);
} catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) {
if(HttpStatus.NOT_FOUND.equals(httpClientOrServerExc.getStatusCode())) {
// your handling of "NOT FOUND" here
// e.g. throw new RuntimeException("Your Error Message here", httpClientOrServerExc);
}
else {
// your handling of other errors here
}
The org.springframework.web.client.HttpServerErrorException is added here for the errors with a 50x.
Now you´re able to simple react to all the StatusCodes you want - except the appropriate one, that matches your HTTP method - like GET and 200, which won´t be handled as exception, as it is the matching one. But this should be straight forward, if you´re implementing/consuming RESTful services :)
If you want all the HTTPStatus from a RestTemplate including 4XX and 5XX, you will have to provide an ResponseErrorHandler to the restTemplate, since the default handler will throw an exception in case of 4XX or 5XX
We could do something like that :
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
#Override
public boolean hasError(HttpStatus statusCode) {
return false;
}
});
ResponseEntity<YourResponse> responseEntity =
restTemplate.getForEntity("http://your.url.here", YourResponse.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.XXXX);
private RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(url,HttpMethod.GET, requestEntity,String.class);
response contains 'body', 'headers' and 'statusCode'
to get statusCode : response.getStatusCode();
exchange(...) works but if you want less code, you can use
org.springframework.boot.test.web.client.TestRestTemplate.getForEntity(...)
which returns an Entity containing StatusCode. Change your example code to this:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
HttpStatus statusCode = response.getStatusCode();
To test it you can use this snippet from my unit test:
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
assertResponseHeaderIsCorrect(response, HttpStatus.OK);
/**
* Test the basics of the response, non-null, status expected, etc...
*/
private void assertResponseHeaderIsCorrect(ResponseEntity<String> response, HttpStatus expectedStatus) {
assertThat(response).isNotNull();
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON_UTF8);
assertThat(response.getStatusCode()).isEqualTo(expectedStatus);
}
There can be some slightly trickier use cases someone might fall in (as I did). Consider the following:
Supporting a Page object in order to use it with RestTemplate and ParameterizedTypeReference:
RestPageResponse:
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
public class RestResponsePage<T> extends PageImpl<T>{
private static final long serialVersionUID = 3248189030448292002L;
public RestResponsePage(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public RestResponsePage(List<T> content) {
super(content);
}
public RestResponsePage() {
super(new ArrayList<T>());
}
}
Using ParameterizedTypeReference will yield the following:
ParameterizedTypeReference<RestResponsePage<MyObject>> responseType =
new ParameterizedTypeReference<RestResponsePage<MyObject>>() {};
HttpEntity<RestResponsePage<MyObject>> response = restTemplate.exchange(oauthUrl, HttpMethod.GET, entity, responseType);
Calling #exchange:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<?> entity = new HttpEntity<>(headers);
response = restTemplate.exchange("localhost:8080/example", HttpMethod.GET, entity, responseType);
Now here is the "tricky" part.
Trying to call exchange's getStatusCode will be impossible because the compiler, unfortunately, will be unaware of the "intended" type of response.
That is because generics are implemented via type erasure which removes all information regarding generic types during compilation (read more - source)
((ResponseEntity<RestResponsePage<MyObject>>) response).getStatusCode()
In this case, you have to explicitly cast the variable to the desired Class to get the statusCode (and/or other attributes)!
Putting this much of code is enough for me
HttpStatus statusCode = ((ResponseEntity<Object>) responseOfEsoft).getStatusCode();
You can use this solution
RestTemplate restTemplate = new RestTemplate();
final String baseUrl = "http://www.myexampleurl.com";
URI uri = new URI(baseUrl);
ResponseEntity<String> result = restTemplate.getForEntity(uri, String.class);
//get status code
int statuCode = result.getStatusCodeValue();
Was able to solve this through:
HttpEntity<Object> entity = restTemplate.getForEntity(uri, Object.class);
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
System.out.println(result.getStatusCode());

How do I read the response header from RestTemplate?

I am posting information to a web service using RestTemplate.postForObject. Besides the result string I need the information in the response header. Is there any way to get this?
RestTemplate template = new RestTemplate();
String result = template.postForObject(url, request, String.class);
Ok, I finally figured it out. The exchange method is exactly what i need. It returns an HttpEntity which contains the full headers.
RestTemplate template = new RestTemplate();
HttpEntity<String> response = template.exchange(url, HttpMethod.POST, request, String.class);
String resultString = response.getBody();
HttpHeaders headers = response.getHeaders();
Best thing to do whould be to use the execute method and pass in a ResponseExtractor which will have access to the headers.
private static class StringFromHeadersExtractor implements ResponseExtractor<String> {
public String extractData(ClientHttpResponse response) throws
{
return doSomthingWithHeader(response.getHeaders());
}
}
Another option (less clean) is to extend RestTemplate and override the call to doExecute and add any special header handling logic there.
HttpEntity<?> entity = new HttpEntity<>( postObject, headers ); // for request
HttpEntity<String> response = template.exchange(url, HttpMethod.POST, entity, String.class);
String result= response.getBody();
HttpHeaders headers = response.getHeaders();
I don't know if this is the recommended method, but it looks like you could extract information from the response headers if you configure the template to use a custom HttpMessageConverter.

Categories

Resources