301 ResponseCode in RestTemplate Client - spring

I am trying to consume a service using RestTemplate in Spring. I tried around and each time getting 301 as response code. However, I am good to get a json response from the same uri while using POSTMAN.
Here is the code to get the statelist:
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
String baseUri = "http://www.whizapi.com/api/v2/util/ui/in/indian-states-list";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUri)
.queryParam("project-app-key","my-key");
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> stats = restTemplate.exchange(builder.build().toUri(),
HttpMethod.GET,entity ,String.class);
Please suggest if I am missing anything.

HTTP 301 is a permanent redirect. You are accessing an outmoded url.
Having checked this url http://www.whizapi.com/api/v2/util/ui/in/indian-states-list redirected to https://www.whizapi.com/api/v2/util/ui/in/indian-states-list.ashx.
The same can be seen in Location response header as shown in the below image.
So, you should directly request https://www.whizapi.com/api/v2/util/ui/in/indian-states-list.ashx from your restTemplate object.
More details could be found here
Hope this helps!

Related

Kerberos client - kerberoRestTemplate not working

Im trying to consume an api which is authenticating with Kerberos. I have referred the below spring documentation related to KerberosRestTemplate.reference link, im passing the correct keytab file and the userPrincipal values as mentioned in the reference doc. But still im receiving 401 from the server.
But when I execute the kinit command in the terminal it receives a ticket from KDC and with that, im able to execute the curl command and get a working response.
KerberosRestTemplate kerberosRestTemplate = new KerberosRestTemplate("svc_dfsd.keytab", "svc_dfsd#sswe.AD");
String url="https://wexample.com:20550/aggr_subscriber_summary_hbase/03434809824";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = kerberosRestTemplate.exchange(url, HttpMethod.GET, entity, String.class);
Can you suggest any other better approach to do this or fix this. All your comments are highly appreciated!!!
Troubleshooting Kerberos might be tricky since the errors are often misleading and Java implementation does a lot if implicit actions (canonicalization of URLs, etc.).
I suggest trying Kerb4J library which allows you to generate the kerberos token explicitly:
SpnegoClient spnegoClient = SpnegoClient.loginWithKeyTab("svc_dfsd#sswe.AD", "svc_dfsd.keytab");
SpnegoContext context = spnegoClient.createContext("https://wexample.com"); // Will result in HTTP/wexample.com SPN
RestTemplate restTemplate = new RestTemplate();
String url="https://wexample.com:20550/aggr_subscriber_summary_hbase/03434809824";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_XML));
headers.add("Authorization", context.createTokenAsAuthroizationHeader());
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = restTemplate .exchange(url, HttpMethod.GET, entity, String.class);
If default SPN resolution works for you, you can also use SpnegoRestTemplate from this Kerb4J:
SpnegoClient spnegoClient = SpnegoClient.loginWithKeyTab("svc_dfsd#sswe.AD", "svc_dfsd.keytab");
SpnegoRestTemplate spnegoRestTemplate = new SpnegoRestTemplate(spnegoClient);
String url="https://wexample.com:20550/aggr_subscriber_summary_hbase/03434809824";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = spnegoRestTemplate.exchange(url, HttpMethod.GET, entity, String.class);
Disclaimer: I'm the author of Kerb4J

Spring Boot RestTemplate WebClient - Response Body JSON Empty

I am using Rest Template and have used Web Client as well. I get the key in response but the body is empty always. Using Postman I can see the Response which is a JSON.
My code snippet is below -
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("john", "doe");
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(
"https://www.getMeData.com",
HttpMethod.GET, request, String.class);
System.out.println(response.getBody());
Response is as below -
{"result":[]}
Has anyone faced the same issue?
In Postman the Response Headers I can see is -
KEY VALUE
Content-Encoding gzip
Content-Type application/json;charset=UTF-8
Transfer-Encoding chunked
I just changed the Restemplate exchange method parameter from String url to new URI("url") and it worked.
ResponseEntity<String> response = restTemplate.exchange(new URI("https://www.getMeData.com"),
HttpMethod.GET, request, String.class);
Try to add headers as below:
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("john", "doe");
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<String>(headers);
For gZip - use
Gzip is disabled by default, check whether it's enabled on your application. If not enable Gzip with properties
server.compression.enabled=true
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
server.compression.min-response-size=1024
And add below encoding to test
headers.add(HttpHeaders.CONTENT_ENCODING, "gzip");
headers.add(HttpHeaders.ACCEPT_ENCODING, "gzip");
You can also try with creating a new Http message convertor and add the same to RestTemplate instance before invoking remote api call..
Spring implementation simply does not support it.
From the HTTP 1.1 2014 Spec:
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.
Having that stated this is how you should refactor your code to make it work:
ResponseEntity<String> response = restTemplate.exchange(
"https://www.getMeData.com",
HttpMethod.POST, // <- this is it
request, String.class);
If you have no control over controller method layout - switch to python + requests library to have this done.

400 Bad Request for Spring RestTemplate and GET on Salesforce.com Connected Apps

Having an issue to create a basic API to the Salesforce.com object queries using Connected Apps and a Spring RestTemplate client. The inital authentication step using a POST operation is working fine and returns the Salesforce instance URL as well as the required access-token. The following GET operation fails with an Error 400. The URL and access-token were both validated by a Chrome POSTMAN plugin, that in combination returns a valid JSON response.
It is also worth to mention that the traditional implementation has not worked well and that "setAccept" was a later attempt to try to solve the parsing of the mediatype.
Below the code in error:
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.parseMediaType("application/json")));
headers.add("User-Agent", USER_AGENT);
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> responseBodyForGet = restRetrieveSalesforceData
.exchange(url, HttpMethod.GET, request, String.class);
The solution found to fix the 400 bug at the Spring RestTemplate GET operation is the following:
URI targetUrl = UriComponentsBuilder.fromUriString(url).build().toUri();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<String> request = new HttpEntity<String>(headers);
logger.info("Requesting GET from: " + url);
ResponseEntity<String> responseBodyForGet = restRetrieveSalesforceData.exchange(targetUrl,
HttpMethod.GET, request, String.class);
I've added the solution to the report posted above. Observe the use of the first statement UriComponentsBuilder, which solved the 400 issue at the GET.

Spring RestTemplate receives "401 Unauthorized"

I am using the following to retrieve JSON via RestTemplate in Spring 4:
protected DocInfoResponse retrieveData(String urlWithAuth) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + auth.getSig());
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<DocInfoResponse> response = restTemplate.exchange(urlWithAuth, HttpMethod.GET, request, DocInfoResponse.class);
return response.getBody();
}
I used the same code (with different response class) to successfully get a JSON doc from the same site (with different parameters to get a different doc).
When I execute the above code I receive the following stack trace (in part):
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
Can anyone point me to why this might be receiving the exception?
I found that my issue originally posted above was due to double encryption happening on the auth params. I resolved it by using UriComponentsBuilder and explicitly calling encode() on the the exchange().
SyncResponse retrieveData(UriComponentsBuilder builder) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<SyncResponse> response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, request, SyncResponse.class);
return response.getBody();
}
My UriComponentsBuilder was built using:
UriComponentsBuilder buildUrl(String urlString) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(urlString);
return auth.appendAuth(builder);
}
(The auth.appendAuth() adds additional .queryParams() needed by the target service in urlString.)
The call to execute this was retrieveData(buildUrl(urlString));.
After investigating on my own problem, I realized that FireFox RESTClient was successful because I was connected to the target URL. The Basic Auth I thought I was using, was not so basic after all.
Eventually, I read the doc of the app i was trying to connect to and realized they propose a connection token mechanism. Now it works.
After reading your code, I say it looks quite OK, although I'm not sure what is your object auth on which you call getSig.
First things first: try to access your service from any client, like a web browser, a PostMan or RESTClient. Make sure you successfully retrieve your infos WITHOUT being connected to your app!!!
Depending on the result, I say you should, either try to encrypt manually your Authorization token (you'll easilly find posts on this site to show you how to) or try another connection mechanism.
The process of creating the Authorization header is relatively straightforward for Basic Authentication, so it can pretty much be done manually with a few lines of code:
HttpHeaders createHeaders(String username, String password){
return new HttpHeaders() {{
String auth = username + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(Charset.forName("US-ASCII")) );
String authHeader = "Basic " + new String( encodedAuth );
set( "Authorization", authHeader );
}};
}
Then, sending a request becomes just as simple:
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange
(uri, HttpMethod.POST, new HttpEntity<T>(createHeaders(username, password)), clazz);
https://www.baeldung.com/how-to-use-resttemplate-with-basic-authentication-in-spring#manual_auth

Spring RestTemplate session

I'm trying to use spring rest template to do a post request to login in.
When I receive the response in my first request i store my session id which is received via cookie. I retrieve it in a set-cookie response header which i get via:
//first request
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
LinkedMultiValueMap<String, Object> mvm = new LinkedMultiValueMap<String, Object>();
mvm.add("LoginForm_Login", "login");
mvm.add("LoginForm_Password", "password");
ResponseEntity<String> result = restTemplate.exchange(uriDWLogin, HttpMethod.POST, requestEntity, String.class);
result.getHeaders().get("Set-Cookie").stream().forEach(System.out::println);
then in every subsequent request i set the Cookie request header with the values received in the first request:
//subsequent request
RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders();
headers.set("Cookie",cookies.stream().collect(Collectors.joining(";")));
HttpEntity<String> entity = new HttpEntity<String>(headers);
RestTemplate.exchange("http://url", HttpMethod.POST, entity, String.class);
all goes well for the second request, but I can not keep the session for the others requests
You will need to use some kind of cache to store your access token.
When you'll be accessing downstream service, you take the token from cache. If cache doesn't contain token, you will authenticate and retrieve it and store to cache first.
Caching is always tricky topic, because it has to be thread-safe. I would try to avoid servlet sessions. You are consuming service, not being consumed.
There are various caching options, but as you are already using Spring, spring caching may be good fit. Take a look at this Spring Cache guide to start.

Resources