I'm using this AWS Object ApiRequest.
I've a url endpoint: url = "https://...com/songs"
NOT WORKING REQUEST(response 500):
ApiRequest request1 = new ApiRequest().withPath(url + "?userId=s123")
.withHttpMethod(HttpMethodName.GET);
WORKING REQUEST(response 200):
Map<String, String> mapParams = new HashMap<>();
mapParams.put("userId", "s123");
ApiRequest request1 = new ApiRequest().withPath(url)
.withHttpMethod(HttpMethodName.GET)
.withParameters(mapParams);
This is the ApiRequest object documentation:
https://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/index.html?com/amazonaws/mobileconnectors/apigateway/ApiRequest.html
Why doesn't it work when using the userId in the query string(url) vs as a parameter? What is the difference between withParameters to query string?
Here userId is passed as a path variable
BASE_URL + "?userId=s123"
and Here userId is passed as a query parameter
mapParams.put("userId", "s123");
The path variable is not working because the API is designed to get params via query params not using path variables. They have mentioned function
withParameters(mapParams)
for accepting the params.
Url params and query parameters are different. The query params are within the URL. HTTP Headers are NOT part of the URL. They're part of the HTTP Message. Maybe this could help;
What is the difference between HTTP parameters and HTTP headers?
Related
i am trying to sign a http request to aws api gateway in android using okhttp. i have more or less used the code in this stackoverflow question stackoverflow question
i use CognitoCachingCredentialsProvider() to get a credentialsProvider object. i then use getCredentials() to get the credentials. i then use the following: credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey() and credentials.getSessionToken() to get the necessary keys and token. i use them in postman and am able to successfully execute the api gateway.
the request fails in android using okhttp, returning a code 403 with the message "Missing Authentication Token".
this is how i prepare the request: i build a DefaultRequest object, setting the endpoint and httpmethod. i then use AWS4Signer to sign the request, passing the credentials object as the signer.sign(defaultRequest, credentials) parameter.
i get a map of headers by calling getHeaders() on the defaultRequest. i create two lists, one called key for the key and one called value for the value. i then loop through the map, loading the keys and corresponding values into the two lists.
i then build my okhttp request as follows:
Request request = new Request.Builder()
.url(my ApiEndPoint)
.addHeader(key.get(0), value.get(0))
.addHeader(key.get(1), value.get(1))
.addHeader(key.get(2), value.get(2))
.addHeader(key.get(3), value.get(3))
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.post(body)
.build();
i notice the following:
in the headers map, key x-amz-security-token has a value ....ending in hKADF87VZ44w9IvZ1gU=
printing out the okhttp request, the key x-amz-security-token has a value .... ending in hKADF87VZ44w9IvZ1gU\u003d
the = is replaced by \u003d, could this be the problem? if so, how to prevent this?
otherwise, any help in solving this problem will be greatly appreciated.
thanks
managed to solve the problem. seems that assigning the headers to the OkHttp request was the problem. so here's my code:
i first get AWSSessionCredentials credentials. then:
AmazonWebServiceRequest amazonWebServiceRequest = new AmazonWebServiceRequest() {
};
String API_GATEWAY_SERVICE_NAME = "execute-api";
com.amazonaws.Request requestAws = new DefaultRequest(amazonWebServiceRequest, API_GATEWAY_SERVICE_NAME);
you can use either the service endpoint:
URI uri = URI.create("https://apigateway.eu-west-1.amazonaws.com");
or your api url (the invoke url for api as per Api Gateway console Stages option (The deployed api)):
String invokeUrl = "https://xxxx.execute-api.eu-west-1.amazonaws.com/yyy/zzzzz";
// using the invoke url
URI uri = URI.create(invokeUrl);
requestAws.setEndpoint(uri);
requestAws.setResourcePath(invokeUrl);
requestAws.setHttpMethod(HttpMethodName.POST);
now sign the request
AWS4Signer signer = new AWS4Signer();
signer.setServiceName(API_GATEWAY_SERVICE_NAME);
signer.setRegionName(Region.getRegion(Regions.EU_WEST_1).getName());
signer.sign(requestAws, credentials);
get the headers
// get map of headers
Map<String, String> headers = requestAws.getHeaders();
// create objects for the headers to add manually in OkHttp request builder
String x_date = null;
String x_token = null;
String authorization = null;
//get and assign values
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().equals("x-amz-security-token")) {
x_token = entry.getValue();
}
if (entry.getKey().equals("X-Amz-Date")) {
x_date = entry.getValue();
}
if (entry.getKey().equals("Authorization")) {
authorization = entry.getValue();
}
}
build the OkHttp request:
Request request = new Request.Builder()
.url(invokeUrl)
.addHeader("Content-Type", "application/json")
.addHeader("X-Amz-Date", x_date)
.addHeader("x-amz-security-token", x_token)
.addHeader("Authorization", authorization)
.post(body)
.build();
now make your OkHttp call.
hope this is helpful to someone.
How to make HTTP GET request in URL that has params appended by & using Retrofit in Android?
URL: http://api.com?name=remote&class=TV
Currently, i am using:
#GET("http://api.com?name={name}&class={class}")
Call<CustomType> get(
#Path(value = "name",encoded = false) String name,
#Path(value = "class",encoded = false) String class);
I am getting following error:
java.lang.IllegalArgumentException: URL query string "name={name}&class={class}"
must not have replace block.
For dynamic query parameters use #Query.
It's a standard GET request url. Just use #Query:
#GET("http://api.com")
Call<CustomType> get(
#Query("name") String name,
#Query("class") String classs);
It will access url:
http://api.com?name=remote&class=TV
I'm writing an Android app, which is a client to my web application. I'm trying to use RoboSpice to perform network requests.
First of all I decided to test an API call to obtain an OAuth2 token. The following curl command can be called to obtain it from command line:
curl -X POST -d "grant_type=password&username=user&password=pass" http://testid:testsecret#localhost:8000/oauth2/token/
user and pass are the credentials for a registered user and testid and testsecret are the id and secret of a registered app in my web application. This call returns a JSON object with a token and other parameters.
I'm trying to do the same request using RoboSpice. Here's the code I wrote for the request:
public class OAuth2Request extends SpringAndroidSpiceRequest<String> {
private final String user;
private final String pass;
public OAuth2Request(String user, String pass) {
super(String.class);
setRestTemplate(new RestTemplate());
getRestTemplate().getMessageConverters().add(new StringHttpMessageConverter());
this.user = user;
this.pass = pass;
}
#Override
public String loadDataFromNetwork() throws RestClientException {
String client_id = "testid";
String client_secret = "testsecret";
HttpBasicAuthentication authHeader = new HttpBasicAuthentication(client_id, client_secret);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
requestHeaders.setUserAgent("AndroidNotesApp/1.0");
String data = String.format("grant_type=password&username=%s&password=%s", this.user, this.pass);
HttpEntity<String> requestEntity = new HttpEntity<>(data, requestHeaders);
String url = "http://10.0.2.2:8000/oauth2/token/";
return getRestTemplate().postForObject(url, requestEntity, String.class);
}
}
The SpiceManager in my activity is declared like:
protected SpiceManager spiceManager = new SpiceManager(JacksonSpringAndroidSpiceService.class);
and the request is made by the following lines:
OAuth2Request req = new OAuth2Request(user, pass);
spiceManager.execute(req, new OAuth2RequestListener());
user and pass are Strings, which get their values from EditText views.
But when I try to run this request, I get an exception 400 BAD REQUEST.
I set up logging in my django app to print the requests which come to /oauth2/token/, and I see, that POST parameters are empty in this request (I expect them to be the same as during the curl request, something like {'grant_type': 'password', 'password': 'pass', 'username': 'user'}).
Why are POST parameters empty in case of RoboSpice request? What am I doing wrong?
P.S. Just in case: the oauth2 authentication in my django web application is implemented using DjangoOAuthToolkit with DjangoRestFramework.
UPDATE: I decided to setup nginx proxy before my django web application to log the request body. The request body I get from the Android app is the following:
\x22grant_type=password&username=user&password=pass\x22
So the strange \x22 symbol is added in the beginning and in the end of the body (I believe it is a double-quote " symbol, but I'm not sure). Seems that these \x22 screw up POST parameter parsing in django. How can I get rid of these symbols?
I managed to solve my problem, so I'm posting an answer in case it helps someone.
SpringAndroidSpiceRequest by default tries to map a java object into JSON, so when I tried to send a String in request body, it wrapped it in double quotes to make it a JSON string. I don't need to send a request body as a JSON string, and in order to do that I need to define additional message converters.
Strangely, these lines in constructor don't seem to do anything
setRestTemplate(new RestTemplate());
getRestTemplate().getMessageConverters().add(new StringHttpMessageConverter());
When I used debugger, it showed just one message converter, MappingJacksonHttpMessageConverter. So I decided to add my own message converters in loadDataFromNetwork method.
I needed two message converters: FormHttpMessageConverter, which will process request and make a request POST body from MultiValueMap, and MappingJacksonHttpMessageConverter, which will process the JSON response into OAuth2Token POJO, which I also declared.
I believe, that for simple testing of REST API with client (POST plain strings and receive plain strings) it'll be better to choose another implementation for SpiceRequest other than SpringAndroidSpiceRequest, but I decided to stick with it, as it'll be easier to implement the complete client for my web application.
The complete code for OAuth2Request:
public class OAuth2Request extends SpringAndroidSpiceRequest<OAuth2Token> {
private final String user;
private final String pass;
public OAuth2Request(String user, String pass) {
super(OAuth2Token.class);
this.user = user;
this.pass = pass;
}
#Override
public OAuth2Token loadDataFromNetwork() throws RestClientException {
String client_id = "testid";
String client_secret = "testsecret";
HttpBasicAuthentication authHeader = new HttpBasicAuthentication(client_id, client_secret);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
requestHeaders.setUserAgent("AndroidNotesApp/1.0");
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
data.add("grant_type", "password");
data.add("username", this.user);
data.add("password", this.pass);
HttpEntity<?> requestEntity = new HttpEntity<>(data, requestHeaders);
String url = "http://10.0.2.2:8000/oauth2/token/";
getRestTemplate().getMessageConverters().clear();
getRestTemplate().getMessageConverters().add(new FormHttpMessageConverter());
getRestTemplate().getMessageConverters().add(new MappingJacksonHttpMessageConverter());
return getRestTemplate().postForObject(url, requestEntity, OAuth2Token.class);
}
}
I have a method on the Server side which gives me information about an specific name registered in my database. I'm accessing it from my Android application.
The request to Server is done normally. What I'm trying to do is to pass parameter to the server depending on the name I want to get.
Here's my Server side method:
#RequestMapping("/android/played")
public ModelAndView getName(String name) {
System.out.println("Requested name: " + name);
........
}
Here's the Android request to it:
private Name getName() {
RestTemplate restTemplate = new RestTemplate();
// Add the String message converter
restTemplate.getMessageConverters().add(
new MappingJacksonHttpMessageConverter());
restTemplate.setRequestFactory(
new HttpComponentsClientHttpRequestFactory());
String url = BASE_URL + "/android/played.json";
String nome = "Testing";
Map<String, String> params = new HashMap<String, String>();
params.put("name", nome);
return restTemplate.getForObject(url, Name.class, params);
}
In the server side, I'm only getting:
Requested name: null
Is it possible to send parameters to my Server like this?
The rest template is expecting a variable "{name}" to be in there for it to replace.
What I think you're looking to do is build a URL with query parameters you have one of two options:
Use a UriComponentsBuilder and add the parameters by that
String url = BASE_URL + "/android/played.json?name={name}"
Option 1 is much more flexible though.
Option 2 is more direct if you just need to get this done.
Example as requested
// Assuming BASE_URL is just a host url like http://www.somehost.com/
URI targetUrl= UriComponentsBuilder.fromUriString(BASE_URL) // Build the base link
.path("/android/played.json") // Add path
.queryParam("name", nome) // Add one or more query params
.build() // Build the URL
.encode() // Encode any URI items that need to be encoded
.toUri(); // Convert to URI
return restTemplate.getForObject(targetUrl, Name.class);
Change
String url = BASE_URL + "/android/played.json";
to
String url = BASE_URL + "/android/played.json?name={name}";
because the map contains variables for the url only!
I'm trying to send a post request using the google api client library but not able to succeed.
This is the snippet I'm using
UrlEncodedContent urlEncodedContent = new UrlEncodedContent(paramMap); //paramMap contains email and password keypairs
HttpRequest request = httpRequestFactory.buildPostRequest(new GenericUrl(Constants.PHP_SERVICE_BASE_PATH + mPath) , urlEncodedContent);
String response = request.execute().parseAsString();
I do not get the expected response. I think it is because the post parameters i.e email and password are not being sent in the correct format. I need to send them in JSON.
NOTE : I'm not using the library for a google web service.
I'm using a Map as input of the JSON. The map is input for the JsonHttpContent used by the post request.
Map<String, String> json = new HashMap<String, String>();
json.put("lat", Double.toString(location.getLatitude()));
json.put("lng", Double.toString(location.getLongitude()));
final HttpContent content = new JsonHttpContent(new JacksonFactory(), json);
final HttpRequest request = getHttpRequestFactory().buildPostRequest(new GenericUrl(url), content);
UrlEncodedContent is used for posting HTTP form content (Content-Type: application/x-www-form-urlencoded). If the Content-Type is application/json you should probably use
http://code.google.com/p/google-http-java-client/source/browse/google-http-client/src/main/java/com/google/api/client/http/json/JsonHttpContent.java