I'm doing some tests using data to register a new user into my DB sending a json by post. The server returns a static string just for testing. I'm getting the proper response using the console and CURL
curl --data '' http://127.0.0.1:3000/api/user/register
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>
and even with the POSTMAN extension sending a post request to that same URL I'm getting a proper response as well. But the code below is not getting any response from the server. What's missing?. It's not throwing any error and the json is correct after checking the debugger(it shouldn't matter but just in case anyone wants to know), just a regular string. The debugger fails on this line
Response response = client.newCall(request).execute();
private void createUser(String name, String email, String password){
//create JSON
final String userJson = formatUserAsJSON(name,email,password);
//send JSON
new AsyncTask<Void,Void,String>(){
#Override
protected String doInBackground(Void... voids) {
try {
//1.create client Object
OkHttpClient client = new OkHttpClient();
//2.Define request being sent to server
RequestBody postData=new FormBody.Builder()
.add("json",userJson)
.build();
Request request=new Request.Builder()
.url("http://127.0.0.1:3000/api/user/register")
.post(postData)
.build();
//3.Transport the request and wait for response to process next
Response response = client.newCall(request).execute();
String result = response.body().string();
return result;
} catch (Exception e) {
Log.d("error_connection","couldn't connect to the API");
return null;
}
}
}.execute();
}
I would appreciate some feedback on this problem. Thank you very much
EDIT: The server is not giving any response cause it's not being reached by the android app. I'm seeing in the ubuntu console and nodejs is not printing any message of any request.....
EDIT 2: In the Android manifest I have the user permission to access the network so it's not that.....
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
You are sending your JSON inside a FormBody
try this:
RequestBody postData = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), userJson);
I solved the problem putting the network IP(it also works if you put the Public internet IP, I've tried it) of the server cause 127.0.0.1 was refering to the Genymotion emulator itself, silly me :S. But that was the answer. Thanks everyone for their support.
Related
I currently try to write a android app to setup and controll a ESP8266 on which micropython runs.
On the micropython server I initialize a websocket like this:
def __init__(self, task_manager, setup_mode):
address = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
self._socket = socket.socket()
self._socket.bind(address)
self._socket.listen(1)
self._socket.setblocking(False)
self._socket.settimeout(5)
self._task_manager = task_manager
self._setup_mode = setup_mode
print('New Socket is listening on: ', address)
And then simple listen to incoming connections like this, and then react to the incoming messages. Also the listing is looped to allow the microcontroller logic to update every 5 seconds.
client, address = self._socket.accept()
print("New request from:", address)
Everything is working fine when I send test request using python from my PC. For example a simple request would be something like this:
data = json.dumps({'load': {'type': "is_lighthub", 'data': {}}})
response = requests.post(ip, json=data)
However when I try to make the same post request using OkHttp from an android app, then there is no incoming connection at the ESP.
Here is the android java code:
private void addIfLighthub(final InetAddress address) {
try {
RequestBody body = RequestBody.create(JSON, "{\"load\": {\"type\": \"is_lighthub_server\", \"data\": {}}");
Request request = new Request.Builder()
.url("http://" + address.getHostAddress())
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
final JSONObject myResponse = new JSONObject(response.body().string());
if((boolean)myResponse.get("is_lighthub")) {
onlineDeviceList.add(address);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
} catch (JSONException jsonException) {
System.out.println(jsonException.getMessage());
}
}
The odd thing however is that that sample code, if provided with for example the address of my router, does receive the routers default html site ...
So, am I missing something? I fairly new to networking but a simple post request from the phone should be the same as from a python sample code, right?
Or is there a error in my java function?
Thank you guys in advance for the help!
If fixed it myself!
The mistake was that the python test client sended the json seperate, while the okhttp client sended both in one piece.
That made the server timeout while waiting for a second message ...
I am working on an Android app in which a log in post request is made to a webservice. The request returns a cookie which expires in 20 minutes.
Using okhttp3 and this PersistentCookieStore library, I got the cookie to be stored and subsequently added it as request header to access authentication-required get requests (e.g. personal information that are non-public).
The code goes this way,
CookieJar myCookieJar = new PersistentCookieJar(new SetCookieCache(),
new SharedPrefsCookiePersistor(this));
OkHttpClient client = new OkHttpClient.Builder().cookieJar(HttpRequests.cookieJar).build();
I then call a method like this inside an (after I have gone through another log in Async task to get the cookie) Async task to perform a get request that requires authentication,
public static String PostReq(String url, String json) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.addHeader("Cookie", "key=value")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
catch(Exception e){
}
}
The .addHeader("Cookie", "key=value") adds the cookie to the header to tell the webservice that I am authenticated.
Here comes my difficulty. Since the cookie expires after 20 minutes, I would like to be able to access the cookie itself to check for the expiration time and possibly redirect the user to the log in activity by calling the method,
myCookie.expiresAt()
and comparing it to
System.currentTimeMillis()
I tried to look at the PersistentCookieStore codes and found that it uses a SharedPreference with the key "CookiePersistence". I looked inside this file while my emulator was running the app and found it to be empty however.
How would I be able to access this cookie that I have obtained? Much thanks for any advice to be given.
OK, this is old, but I was facing the same problem, and here is how I fixed it.
Hold a reference to your SetCookieCache used to instantiate your CookieJar:
SetCookieCache cookieCache = new SetCookieCache();
CookieJar myCookieJar = new PersistentCookieJar(
cookieCache,
new SharedPrefsCookiePersistor(this)
);
Then use this to find your cookie and check it:
for (Cookie cookie : cookieCache) {
if (cookie.name().equals("cookie_name") && cookie.persistent()) {
//cookie is still good
break;
}
}
Or use cookie.expiresAt() to do your thing.
https://www.instagram.com/developer/authentication/
Im on the last step (three). Im making a GET request and Im getting a response but it is now successful, im getting code 400 saying im missing client_id but I am not missing it. I saw a few others with the same problem but no clear solution. Below is the relevant code please have a look im a little bit new to retrofit.
//here I am creating the request to send through retrofit...
<!-- language: java -->
TokenRequest request = new TokenRequest();
request.setClient_id(Constants.CLIENT_ID);
request.setClient_secret(Constants.CLIENT_SECRET);
request.setGrant_type(Constants.AUTORISATION_CODE);
request.setRedirect_uri(Constants.REDIRECT_URI);
request.setCode(code);
//this is the actual request
final Call<TokenResponse> accessToken = ServiceManager.createTokenService().getAccessToken(request);
accessToken.enqueue(new Callback<TokenResponse>() {
#Override
public void onResponse(Call<TokenResponse> call, Response<TokenResponse> response) {
mtext.setText("hello");
if(response.isSuccess()){
mtext.setText("success");
}else{
try {
mtext.setText(response.errorBody().string());
//Log.d("mylog", response.errorBody().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
At the moment its not going into the if block but showing the error in the else block.
I had my API interface built incorrectly, you can check the comments for the incorrect one. The correct one is the one below:
#FormUrlEncoded #POST("/oauth/access_token")
Call<TokenResponse> getAccessToken(#Field("client_id") String client_id,
#Field("client_secret") String client_secret,
#Field("redirect_uri") String redirect_uri,
#Field("grant_type") String grant_type, #Field("code") String code);
This was really tricky for me being new to retrofit hope it helps if you're stuck at the same problem!
I am attempting to call a put method on my server using OkHttp from an Android application.
This is the api method signature:
public void Put(int userId, string regId)
{
}
This is the Android code to call the above method:
private boolean SendGCMRegIdToServer(String registrationId, Integer userId) throws IOException {
HttpUrl url = new HttpUrl.Builder()
.scheme("http")
.host(serverApiHost)
.addPathSegment("AppDashboard")
.addPathSegment("api")
.addPathSegment("GCM/")
.build();
MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
String json = "{'userId':" + userId + ","
+ "'regId':'" + registrationId + "'"
+ "}";
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.put(requestBody)
.build();
//this should post the data to my server
Response response = client.newCall(request).execute();
if(response.code() == 400)
return false;
return true;
}
Now the problem is I am getting the error code 405 in the response saying Method not allowed, but I cannot see where the problem is because I can successfully call the method using Postman on the server itself as below:
http://localhost/AppDashboard/api/GCM?userId=5®Id=123
I'm thinking it may have something to do with an integer or string being passed incorrectly in the JSON string, but cannot see why this isn't working.
i had the same problem and server was returning 405 . after some search i realized that is a configuration problem on IIS that does not let put requests. so there is no problem in android code and you should config your server to let this kind of requests.
see this , this and this
Ok thanks for replies guys but seems I was getting a little confused between the two methods I was using to pass the params to my API.
Here's what I did:
changed the signature of the method to post with a param [FromBody] as a Model (only supports one paramater)...
public void Post([FromBody]UserGcmRegIdModel model)
{
}
I was then able to change my method call to the following using a nicer JSONBuilder and using .post in the request builder rather than .put
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("UserId", userId);
jsonObject.put("RegId", registrationId);
} catch (JSONException e) {
e.printStackTrace();
}
String json = jsonObject.toString();
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
I still don't know if there is a problem with put() methods on IIS but using a post in my case was absolutely fine so I'm going with that...
I see two different approaches in your REST api calls. In the one of OkHttp you send a PUT method with a JSON object serialized, and in POSTMAN you send a PUT (although I guess you do a GET) request with the parameters within the URL, I mean not in JSON body structure.
Anyway, HTTP 405 is telling you that your backend does not support the PUT method, and probably it's expecting a POST method with the "X-HTTP-Method-Override:PUT" HTTP header since POST is more standard method in REST than PUT.
What would I do is check your POSTMAN request carefully and adjust the one of Android to be the same method, parameters and headers, not more.
Answer Update (as question has been updated)
Of course there is a problem with that verb, as I said above IIS handles only the standard methods and PUT is not one of those. You have three choices:
Change your PUT to POST.
Use POST with X-HTTP-Method-Override to PUT. (reference)
Modify IIS config to support non standard REST methods. I
personally wouldn't suggest the 3rd one, since it's attached to the
backend config (e.g. imagine you change IIS to NancyFX).
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);
}
}