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!
Related
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?
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 am asking this question based on the answers in this link
POST request via RestTemplate in JSON
I actually wanted to send JSON from client and receive the same at REST server. Since the client part is done in the link I mentioned above. For the same how would I handle that request at server end.
CLIENT:
// create request body
JSONObject request = new JSONObject();
request.put("username", name);
request.put("password", password);
// set headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);
// send request and parse result
ResponseEntity<String> loginResponse = restTemplate
.exchange(urlString, HttpMethod.POST, entity, String.class);
if (loginResponse.getStatusCode() == HttpStatus.OK) {
JSONObject userJson = new JSONObject(loginResponse.getBody());
} else if (loginResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) {
// nono... bad credentials
}
SERVER:
#RequestMapping(method=RequestMethod.POST, value = "/login")
public ResponseEntity<String> login(#RequestBody HttpEntity<String> entity) {
JSONObject jsonObject = new JSONObject(entity.getBody());
String username = jsonObject.getString("username");
return new ResponseEntity<>(username, HttpStatus.OK);
}
This gives me 400 bad request error at client side. Hoping for some clues about how to handle this at server side.
HTTPEntity should not be used in your server method. Instead use the argument which is being passed to HTTPEntity from your client. In your case it has to String since you are passing string from client. Below code should work for you.
#RequestMapping(method=RequestMethod.POST, value = "/login")
public ResponseEntity<String> login(#RequestBody String jsonStr) {
System.out.println("jsonStr " + jsonStr);
JSONObject jsonObject = new JSONObject(jsonStr);
String username = jsonObject.getString("username");
return new ResponseEntity<String>(username, HttpStatus.OK);
}
My advice is to create bean class and use it in server and client instead of converting it to String. It will improve readability of the code.
When using the Spring RestTemplate, I usually prefer to exchange objects directly. For example:
Step 1: Declare and define a data holder class
class User {
private String username;
private String password;
... accessor methods, constructors, etc. ...
}
Step 2: Send objects of this class to the server using RestTemplate
... You have a RestTemplate instance to send data to the server ...
// You have an object to send to the server, such as:
User user = new User("user", "secret");
// Set HTTP headers for an error-free exchange with the server.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// Generate an HTTP request payload.
HttpEntity<User> request = new HttpEntity<User>(user, headers);
// Send the payload to the server.
restTemplate.exchange("[url]", [HttpMethod], request, User.class);
Step 3: Configure a ContentNegotiatingViewResolver on the server
Declare a bean of the type ContentNegotiatingViewResolver in the Spring XML or Java configuration. This will help the server automatically bind HTTP requests with bean objects.
Step 4: Receive the request on the server
#RestController
#RequestMapping("/user")
class UserAPI {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public User create(User user) {
// Process the user.
// Possibly return the same user, although anything can be returned.
return user;
}
}
The ContentNegotiatingViewResolver ensures that the incoming request gets translated into a User instance without any other intervention.
Step 5: Receive the response on the client
// Receive the response.
HttpEntity<User> response = restTemplate.exchange("[url]", [HttpMethod], request, User.class);
// Unwrap the object from the response.
user = response.getBody();
You will notice that the client and the server both use the same bean class (User). This keeps both in sync as any breaking change in the bean structure would immediately cause a compilation failure for one or both, necessitating a fix before the code is deployed.
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've been trying to build an Android app on with Foursquare API. I want my users to be anonymous so that there will be no authentication. As stated here:
As covered in our platform docs, our Venues Platform endpoints can be accessed without
user authentication and our Merchant Platform endpoints require the end-user to be an
authed venue manager. All other endpoints, unless otherwise noted, require user
authentication.
I found Venue Categories ("https://api.foursquare.com/v2/venues/categories") are Venues Platform endpoints so I fetched the categories like this:
private static final String API_URL = "https://api.foursquare.com/v2";
private static final String VENUE_CATEGORY = "/venues/categories";
URL url = new URL(API_URL + VENUE_CATEGORY);
Log.d(TAG, "Opening URL " + url.toString());
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.connect();
String response = streamToString(urlConnection.getInputStream());
JSONObject jsonObj = (JSONObject) new JSONTokener(response).nextValue();
JSONObject resp = (JSONObject) jsonObj.get("response");
JSONArray category = resp.getJSONArray("categories");
JSONObject sample = category.getJSONObject(0);
mCategoryName = sample.getString("name");
And I get java.io.FileNotFoundException:https://api.foursquare.com/v2/venues/categories.
Do I still firstly need to fetch access token here?
You are right, you can access the venues/categories endpoint without authentication but in order to do this you need to supply your Client ID and Client Secret as parameters to the API call, so that Foursquare knows it is your app accessing the endpoint.
You can register an app to get a Client ID and Secret here (if you haven't already), then if you add the details to the url, you should be able to access the endpoint:
private static final String API_URL = "https://api.foursquare.com/v2";
private static final String VENUE_CATEGORY = "/venues/categories";
private static final String CLIENT_INFO = "client_id=YOURCLIENTID&client_secret=YOURCLIENTSECRET"
URL url = new URL(API_URL + VENUE_CATEGORY + "?" + CLIENT_INFO);
Hope that helps.