spring android resttemplate encoding url - java

I use Spring android RestTemplate to execute GET request to Youtube API like this:
// build rest template
RestTemplate restTemplate = new RestTemplate();
GsonHttpMessageConverter jsonConverter = new GsonHttpMessageConverter();
FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
final List<HttpMessageConverter<?>> listHttpMessageConverters = restTemplate.getMessageConverters();
listHttpMessageConverters.add(jsonConverter);
listHttpMessageConverters.add(formHttpMessageConverter);
listHttpMessageConverters.add(stringHttpMessageConverter);
restTemplate.setMessageConverters(listHttpMessageConverters);
Uri.Builder uriBuilder = Uri.parse(https://www.googleapis.com/youtube/v3/channels).buildUpon();
uriBuilder.appendQueryParameter("key", API_KEY);
uriBuilder.appendQueryParameter("part", "id,snippet");
uriBuilder.appendQueryParameter("forUsername", channelName);
String url = uriBuilder.build().toString(); // this is right url
// like this: https://www.googleapis.com/youtube/v3/channels?key=MY_KEY&part=id%2Csnippet&forUsername=cnn
MyEntity result = restTemplate.getForObject(url, MyEntity.class);
From debug, I can see RestTemplate execute wrong url and I got 400 bad request error:
03-16 12:06:47.651: W/RestTemplate(24970): GET request for
"https://www.googleapis.com/youtube/v3/channels?key=MY_KEY&part=id%252Csnippet&forUsername=cnn"
resulted in 400 (Bad Request); invoking error handler
I have no idea why RestTemplate try to encode parameter url again, from id%2Csnippet to id%252Csnippet
Is there any way to correct it?

It just so happens that the RestTemplate#getForObject(..) method that expects a String builds a URI from the given String and encodes it before using it. It uses custom Spring classes to do this. (See the source code.)
You can fix this issue by creating a URI object from your String and pass that to the method.
String url = uriBuilder.build().toString(); // this is right url
URI uri = new URI(url);
MyEntity result = restTemplate.getForObject(uri, MyEntity.class);

Related

Unable to send request parameter using Spring RestTemplate client to HttpServeletRequest doPost

I am using Spring RestTemplate client to do a POST call to another application which handles this request as HTTpServletRequest.
Problem is HTTpServletRequest is expecting a key value pair e.g.
String xmlString = request.getParameter("xml12")
//xmlString should be "`<parent><child></child></parent>`" but coming as null.
Here is the code snippet of both ends -
My app -
String data = "`<parent><child></child></parent>`"
HttpHeaders header = new HttpHeaders()
header.setContentType(MediaType.APPLICATION_XML)
Map<String,String> bodyParamMap = new HashMap<String,String>();
bodyParamMap.put("xml123",data)
String reqBodyData = new ObjectMapper().writeValuesAsString(bodyParamMap)
HttpEntity<String> entity = new HttpEntity<String>(reqBodyData,header)
RestTemplate rt = new RestTemplate()
String response = rt.postForObject("url",entity,String.class)
//Getting response as 500
Other app -
HttpServletRequest request = new HttpServletRequest()
String xmlString = request.getParameter("xml123")
// xmlString is null
I just want to know my mistake and how to pass my data string to post request so that request.getParameter("xml123") receives my data xml as String.

Request method 'PUT' not supported When calling from Resttemplate

I have to call a PUT method using Resttemplate. I am able to hit the service from POST Man. But when i try the same request from Java using Resttemplate its throwing error .What could be mistake i am doing.
405 : [{"category":"ACCESS","code":"METHOD_NOT_SUPPORTED","description":"Request method 'PUT' not
supported","httpStatusCode":"405"}]
#Autowired
#Qualifier("orderMasterUpdateClient")
private RestTemplate orderMasterUpdateClient; // Loading the template with credentials and URL
ResponseEntity<SalesOrderDocument> responseEntity = orderMasterUpdateClient.exchange(
URL,
HttpMethod.PUT,
new HttpEntity<>(headers),
SalesOrderDocument.class, changeRequest);
If you want to send changeRequestobject data in the body of the PUT request, I suggest you to use next RestTemplate exchange method call:
String url = "http://host/service";
ChangeRequest changeRequest = new ChangeRequest();
HttpHeaders httpHeaders = new HttpHeaders();
HttpEntity<ChangeRequest> httpEntity = new HttpEntity<>(changeRequest, httpHeaders);
ResponseEntity<ChangeRequest> response = restTemplate
.exchange(url, HttpMethod.PUT, httpEntity, ChangeRequest.class);

How to consume a HTTPS GET service with Spring Boot

I am trying to consume the following HTTPS endpoints from Yahoo Weather Service:
Yahoo Weather Service API
I am doing some special query according to the API to get the current weather at some parametrized location.
#Service("weatherConditionService")
public class WeatherConditionServiceImpl implements WeatherConditionService {
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public WeatherCondition getCurrentWeatherConditionsFor(Location location) {
RestTemplate restTemplate = new RestTemplate();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(URL);
stringBuilder.append("?q=select%20item.condition%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22");
// TODO: Validate YQL query injection
stringBuilder.append(location.getName());
stringBuilder.append("%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys");
WeatherQuery weatherQuery = restTemplate.getForObject(stringBuilder.toString(), WeatherQuery.class);
// TODO: Test Json mapping response
Condition condition = weatherQuery.getQuery().getResults().getChannel().getItem().getCondition();
return new WeatherCondition(condition.getDate(), Integer.parseInt(condition.getTemp()), condition.getText());
}
Location is a class that provides the attribute "name" that is a String description of the location, such as "New York" or "Manila".
Condition an other classes just map the returning object.
When executing I get the following HTTP response:
org.springframework.web.client.HttpClientErrorException: 403 Forbidden
So this means I am not authorized to access the resource from what I understand.
The URL works great if I just copy & paste it in a web browser:
Yahoo Weather Query
I think that mapping is not a problem since I am not getting "400" (Bad Request) but "403" (Forbidden)
There must be some error on the way I use the RestTemplate object. I am researching but I can't find an answer.
The docs say you need an api key. But when I make a call like this:
fetch('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22nome%2C%20ak%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys')
.then(resp=> resp.json())
.then((res)=>console.log(res.query.results))
https://repl.it/NeoM
It works fine without one. Perhaps you've been blackisted for hitting the api too often.
Your code seems fine.
I finally found the answer. It finally WAS a Bad Request because I needed to pass the parameters differently (not as part of the URL).
I found the answer here. Here goes the code for my particular Yahoo Weather API call return a String (I still will have to do some work to use the mapping).
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public String callYahooWeatherApi() {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(URL)
.queryParam("q", "select wind from weather.forecast where woeid=2460286")
.queryParam("format", "json");
HttpEntity<?> entity = new HttpEntity<>(headers);
HttpEntity<String> response = restTemplate.exchange(
builder.build().encode().toUri(),
HttpMethod.GET,
entity,
String.class);
return response.getBody();
}

Spring for Android - Could not extract response: no suitable HttpMessageConverter found for response type

I'm trying to retrieve some data from a REST service using spring for Android.
However I'm running into problems. I'm also using Robospice - and as such have a method like so:
public FunkyThingsList loadDataFromNetwork() throws Exception {
String baseURI =context.getResources().getString(R.string.baseWebServiceURI);
String uri = baseURI + "/FunkyThings/date/" + dateToLookUp.toString("yyyy-MM-dd");
RestTemplate restTemplate = getRestTemplate();
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept( Arrays.asList(MediaType.APPLICATION_JSON));
HttpAuthentication authHeader = new HttpBasicAuthentication(username, password);
headers.setAuthorization(authHeader);
// Create the request body as a MultiValueMap
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
HttpMessageConverter<String> stringConverter = new StringHttpMessageConverter();
FormHttpMessageConverter formConverter = new FormHttpMessageConverter();
List<HttpMessageConverter<?>> msgConverters = restTemplate.getMessageConverters();
msgConverters.add(formConverter);
msgConverters.add(stringConverter);
restTemplate.setMessageConverters(msgConverters);
HttpEntity<?> httpEntity = new HttpEntity<Object>(map, headers);
final ResponseEntity<FunkyThingsList> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity,FunkyThingsList.class);
return responseEntity.getBody();
}
Unfortunately this isn't working. I'm getting the following exception thrown:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [com.MyProject.DataClassPojos.RoboSpiceArrayLists.FunkyThingsList] and content type [application/json;charset=utf-8]
Now based on my Googling I get the feeling I need to add a message converter. I'm just not sure which message converter I need, or where I add it?
For the default JSON HttpMessageConverter, you'll need to add either Jackson 1 or Jackson 2 to your classpath.
Otherwise, you can add some other JSON library and write your own HttpMessageConverter which can do the deserialization. You add it to the RestTemplate. You can either use the constructor or this method.
If you are following the Spring for Android tutorial and you get the same error - this might be of help:
In your MainActivity.java:
...
RestTemplate restTemplate = new RestTemplate();
MappingJacksonHttpMessageConverter converter = new
MappingJacksonHttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_HTML));
restTemplate.getMessageConverters().add(converter);
Use the converter in the call and configure it to use as a Media Type the TEXT_HTML, then use the instance of the converter.
Also the tutorial is a bit outdated so use the latest versions as suggested here in your build.gradle:
dependencies{
//Spring Framework for REST calls and Jackson for JSON processing
compile 'org.springframework.android:spring-android-rest-template:2.0.0.M3'
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.1.3'
}
repositories {
maven {
url 'https://repo.spring.io/libs-milestone'
}
}

Calling an RESTFul using Spring-Android

Here is my Strifified json,
{
"Request":{
"Object1":{
"Key1":"Value1"
},
"Object2":{
"Key2":"Value2"
}
},
"Object3":{
"Key3":"Value3"
}
}
I am forming this using Gson. String Stringifiedjson = new Gson().toJson(user);
Here is what i am trying to achive.
RestTemplate rest = new RestTemplate();
String url = "";
String event = rest.getForObject(url, Stringifiedjson);
How would i send to my REST Service and get back my result in onEventHandler or onErrorHandler. I am basically from JavaScript background.
Why does the method getForObject does not accept String, String as params.
Update:
AuthenticateUser user = new AuthenticateUser(credential, Header);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(new MediaType("application","json"));
//HttpEntity<AuthenticateUser> requestEntity = new HttpEntity<AuthenticateUser>(user, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
String url = "url";
String result = restTemplate.postForObject(url, AuthenticateUser.class, String.class);
Attached is the pastie of what exception i am getting.
http://pastie.org/private/efyfvvbxyxdsvm3lvv7q
About the second question: I just found this example (you could take a look at the entire doc ;) )
2.7.1 Basic Usage Example
The following example shows a query to google for the search term "SpringSource".
String url = "https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q={query}";
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the String message converter
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
// Make the HTTP GET request, marshaling the response to a String
String result = restTemplate.getForObject(url, String.class, "SpringSource");
getForObject
public <T> T getForObject(URI url,
Class<T> responseType)
throws RestClientException
Description copied from interface: RestOperations
Retrieve a representation by doing a GET on the URL . The response (if any) is converted and returned.
Specified by:
getForObject in interface RestOperations
Parameters:
url - the URL
responseType - the type of the return value
Returns:
the converted object
Throws:
RestClientException
The exception in your stacktrace could be related to the same issue of this post. The problem occurs when your app tries to make a connection in the main thread.
10-23 15:46:10.106: E/AndroidRuntime(1038): FATAL EXCEPTION: main
10-23 15:46:10.106: E/AndroidRuntime(1038): android.os.NetworkOnMainThreadException
The NetworkOnMainThreadException is thrown when you execute any network operation in your application main ui thread (see also Keeping Your App Responsive. This is not allowed. You'll have to use a background thread for network operation, see Worker threads on http://developer.android.com/guide/components/processes-and-threads.html

Categories