How to send POST request from Android app to Rails application? - android

I have problem with sending POST request to my rails application from Android app (using Spring for Android)
I have following Rails controller:
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :email)
end
I'm sending POST request (using Spring for Android):
RestTemplate restTemplatePost = new RestTemplate();
restTemplatePost.getMessageConverters().add(new StringHttpMessageConverter());
UserDto user = new UserDto();
user.name = "testName";
user.email = "test#fromandroid.com";
Gson gson = new Gson();
String request = gson.toJson(user);
restTemplatePost.postForObject(createUserUrl, request, String.class);
} catch(RestClientException e) {
Log.e("POST",e.getLocalizedMessage());
}
value of String request is {"email":"test#fromandroid.com","name":"testName"}
and createUserUrl has value of: http://10.0.0.2:3000/users
however, I still get the following error:
> W/RestTemplate(3299): POST request for "http://10.0.0.2:3000/users"
> resulted in 400 (Bad Request); invoking error handler
I'm able to send the GET request, but POST doesn't work. What am I doing wrong?
I used CURL to get better error message and it returns:
param not found: user
so the problem might be with required user and optional name and emails parameters which are accepted by REST API, but how should I format user in json for it to work?
I'd greatly appreciate your help!

Please ensure that your controller config accept POST method, may be it just receives GET.
RailsController
RailsRouting
And you can use RestClient (Plugin of Firefox or Chrome) to test your api.
Rest Client - Firefox
Rest Client - Chrome

There are some issues you must have to follow to post.
you need to post data something like:
{"user_params" : {"email":"test#fromandroid.com","name":"testName"}}
to create a user (according to your controller).
To post data add Request Header. As you sending json data, so header will be Content-Type: application/json.
Also I'm suggesting you to use a FireFox addons is named "RESTClient" to simulate this scenario.
Thanks.

Related

Connection between Android and AWS SageMaker with AWS Lambda

I set up a connection between Android and AWS Lambda which has the endpoint set to SageMaker. I am using the REST API during the connection, the AWS Cognito plug is set to be accessed without authorization.
I make a connection as described here:
https://aws.amazon.com/blogs/machine-learning/call-an-amazon-sagemaker-model-endpoint-using-amazon-api-gateway-and-aws-lambda/
My question is how to send this data:
{"data":"13.49,22.3,86.91,561.0,0.08752,0.07697999999999999,0.047510000000000004,0.033839999999999995,0.1809,0.057179999999999995,0.2338,1.3530000000000002,1.735,20.2,0.004455,0.013819999999999999,0.02095,0.01184,0.01641,0.001956,15.15,31.82,99.0,698.8,0.1162,0.1711,0.2282,0.1282,0.2871,0.06917000000000001"}
And how to view the received response later. Anyone know how to do it or where I can find tips on how to do it?
If I understand correctly, this is your system flow:
POST some data from your Android device
It gets received by API Gateway
And continues through to AWS Lambda
In AWS Lambda the data is extracted, and passed to Sage Maker
Creating a POST using AWS Amplify
To POST data form the Android device, follow the Amplify API (REST) category documentation.
Specifically, you can do something like:
val options = RestOptions.builder()
.addPath("/prod/predictbreastcancer")
.addBody("{\"data\":\"13.49,22.3,86.91,561.0,0.08752,0.07697999999999999,0.047510000000000004,0.033839999999999995,0.1809,0.057179999999999995,0.2338,1.3530000000000002,1.735,20.2,0.004455,0.013819999999999999,0.02095,0.01184,0.01641,0.001956,15.15,31.82,99.0,698.8,0.1162,0.1711,0.2282,0.1282,0.2871,0.06917000000000001\"}".toByteArray())
.build()
Amplify.API.post(options,
{ Log.i("Demo", "POST response = $it") },
{ Log.e("Demo", "POST failed", it) }
)
Creating POST body from EditText content
You mentioned you're using an EditText widget to gather the input data. I assume a user can enter a comma-separated list of values like 0.44, 6.11, etc.
To extract it's content and build the POST body, you can do:
val input = findViewById(R.id.input) as EditText
val body = JSONObject()
.put("data", input.text)
.toString()
.replaceAll("\"", "\\\"")
Displaying response in another Activity
Skimming the blog you referenced, I can't see an example of the response body content. But, here's how you can retrieve response JSON and pass it to a new activity.
Amplify.API.post(options,
{ response ->
val intent = Intent(this, YourOtherActivity::class.java)
intent.putExtra("json", response.data.asString())
runOnUiThread { startActivity(intent) }
},
{ /* handle error ... */ }
)
In YourOtherActivity, access the extra data in onCreate() like so:
val json = intent.getStringExtra("json")

Only receives one cookie in fetch response on Android but not on iOS

When I send a POST fetch request to my website to login it all works on iOS. The fetch response set-cookie have all the cookies I need to proceed and make future requests. The problem is on Android I only receive one cookie in set-cookie, even though there is more. It seems like it gets cut and I can't seem to access the raw response.
My fetch code:
const myRequest = new Request('MYURL',
{method: 'POST', body: parameters,
headers: {
"Referer": 'MYURL.com/login',
'Origin': 'MYURL',
'Content-Type': 'application/x-www-form-urlencoded'
}});
fetch(myRequest)
.then(
function(response) {
try {
console.log("JSON RESPONSE: " + response.json());
console.log(JSON.stringify(response, null, 2));
console.log(response.headers.get('Content-Type'));
console.log(response.headers.get('Date'));
} catch(err) {
console.log("ERROR: " + err);
}
}
)
.catch(function(err) {
console.log('Fetch Error', err);
});
I know that android gets correctly logged in cause the server redirects me to the menu page and also recognizes the clients personal ID.
Here is the response from iOS(the one that works):
"set-cookie": "session=CENSORED; Domain=.MYURL; Path=/; HttpOnly, clientID=123; expires=Thu, 07-Nov-2019 09:27:20 GMT; domain=.MYURL; path=/",
Perfect response. Everything works.
Here is the response from android:
"set-cookie": "clientID=123; expires=Thu, 07-Nov-2019 09:34:00 GMT; domain=.MYURL; path=/",
I've tried for several days to understand the problem but to no avail. I've tried to test cookies without httponly to see if that was the error but no.
I hope some experienced react-native users can shine light on this problem. I haven't experienced it before when I created native apps for android and IOS. Only with react-native. :-(
I hope to get to this to work since I would rather avoid using native code for this small part.
EDIT: Tested fetch function with reddit login api and same problem occurs. Only 1 cookie in response set-cookio on Android but all cookies shown on iOS.
Look into the official documents of React Native and MDN:
https://facebook.github.io/react-native/docs/network
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
The problem here is fetch won't send or receive any cookie from server by default. So you have to enable it by add credentials. And there are some libraries that can help you manage cookies:
https://github.com/joeferraro/react-native-cookies

Azure Easy Apis InvokeApi Method is calling GET instead of POST

I am currently working on creating a custom authentication for a Xamarin.Android app using Azure. I have successfully created my API and it is properly returning values when submitting a raw payload using Advanced REST Client.
I am now trying to implement this on Xamarin.Android using Azure's MobileServiceClient SDK and when using the invokeApi method as demonstrated below in my code, I am getting an exception indicating that it is calling GET instead of POST. Is anyone aware of what I might be doing wrong?
ex.Message returns
"Cannot GET /api/register?username=azureAccountTest&password=testingpassword"
public async Task RegisterAsync()
{
Dictionary<string, string> user = new Dictionary<string, string>
{
{ "username", username },
{ "password", password }
};
try
{
CancellationToken ct;
var result = await client.InvokeApiAsync("register", HttpMethod.Post, user, ct);
}
catch(Exception ex)
{
var message = ex.Message;
}
}
According to your description, I tested this issue on my local side and I could retrieve the authenticationToken as follows:
You used the following method for InvokeApiAsync:
public Task<JToken> InvokeApiAsync(string apiName, HttpMethod method, IDictionary<string, string> parameters, CancellationToken cancellationToken = default(CancellationToken));
Note: It summarizes that the Additional data will sent to through the query string.
Per my understanding, you could refer to the following method for sending additional data though the HTTP content as follows:
JObject user = new JObject();
user.Add("username", "bruce");
user.Add("password", "123456");
var result = await App.MobileService.InvokeApiAsync("/.auth/login/custom", HttpMethod.Post, user, ct);
Additionally, you need to specific the mobileAppUri with https endpoint when deploy to azure side. Here is a similar issue, you could refer to here. Moreover, I would recommend you refer to adrian hall's book about Custom Authentication.
UPDATE:
Based on your comment, I checked the custom authentication and found the following note from adrian hall's book about Custom Authentication:
You must turn on Authentication / Authorization in your App Service. Set the Action to take when request is not authenticated to Allow Request (no action) and do not configure any of the supported authentication providers.

Make Go http.Response verbose all parameters

I am having a problem getting a parameter sent from android app into go application. I called r.FormValue(key) but it returned null. I want to find the way to check what are parameters available on Go side after the android app sent the post data to it. Is there any way to do this, getting all parameters without using keys?
The Request structure in go has a Form field which is populated with request parameters after ParseForm() is called.
Form contains the parsed form data, including both the URL field's
query parameters and the POST or PUT form data.This field is only
available after ParseForm is called. The HTTP client ignores Form and
uses Body instead.
You could try adding the following code after receiving a request:
func(w http.ResponseWriter, request *http.Request) {
request.ParseForm()
log.Printf("%v",request.Form)
//....
}
If this is for debugging, you can use DumpRequest:
func(w http.ResponseWriter, r *http.Request) {
dump, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Printf("%s", dump)
}

JSON causing unprocessable entity when submitting to Rails (Error 422)

I'm using an android app to submit to a Rails app with a Mongo backend hosted on Heroku. I am able to use the form online to submit a new item into the mongo database, but when I try to submit JSON from an android app I get:
{"error":"Unprocessable Entity","status":"422"}
I think this may have to do with the order of JSON when I submit as it seems to have gotten mixed up in order which I realized is normal according to a past SO question. But, the solution on that question did not work for me.
Here is the construction of the JSONObject in my Android app:
JSONObject jsonObject = new JSONObject();
jsonObject.put("ph", 2);
jsonObject.put("chlorine", 2.0);
jsonObject.put("magnified_Link", url);//URLEncoder.encode(encodedImage, "UTF-8"));
jsonObject.put("taste", "yucky");
jsonObject.put("odor", "smelly");
jsonObject.put("temperature", "77.0");
jsonObject.put("mercury", 234);
jsonObject.put("hardness", 9.0);
jsonObject.put("lat", latitude);
jsonObject.put("long", longitude);
String json = jsonObject.toString(); // Output to string
Log.d(TAG, json);
StringEntity se = new StringEntity(json);
// put json string into server
httpPost.setEntity(se);
//httpPost.setHeader("Authorization", "Client-ID " +API_KEY);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
This is how the JSON appears in my android app when I log it before submission:
{"chlorine":2,"odor":"smelly","magnified_Link":"http:\/\/i.imgur.com\/JnbuUwy.jpg","long":0,"hardness":9,"ph":2,"taste":"yucky","lat":0,"mercury":234,"temperature":"77.0"}
This is how my JSON page looks like on my rails app with one successful submission into it through the online rails app:
[{"id":{"$oid":"558766f26633380003000000"},"ph":1.0,"chlorine":2.0,"magnified_Link":"3","taste":"4","odor":"5","temperature":6.0,"mercury":7.0,"hardness":8.0,"lat":40.7127,"long":-74.0059,"url":"https://distributed-health.herokuapp.com/distributed_healths/558766f26633380003000000.json"}]
Finally here is the model for a "DistributedHealth" (my object) in my rails app:
class DistributedHealth
include Mongoid::Document
field :ph, type: Float
field :chlorine, type: Float
field :magnified_Link, type: String
field :taste, type: String
field :odor, type: String
field :temperature, type: Float
field :mercury, type: Float
field :hardness, type: Float
field :lat, type: Float
field :long, type: Float
end
I can include any other relevant code and will make edits to this post as I continue trying to solve this issue.
Thank you,
Clayton.
EDIT: I have the error message from my heroku server
2015-06-23T21:29:34.382730+00:00 heroku[router]: at=info method=POST path="/distributed_healths.json" host=distributed-health.herokuapp.com request_id=2dcde4a4-3dcc-4381-98ba-6ca399400a6d fwd="128.54.58.245" dyno=web.1 connect=3ms service=14ms status=422 bytes=328
The issue ended up being a Rails side one and didn't have to do with the order of the JSON being submitted. I turned out the need for a CSRF token using the line right after class DistributedHealthsController < ApplicationController in my Controller:
skip_before_action :verify_authenticity_token
This isn't recommended as it is a work around security, but if you want to exclude certain methods this post will show you how.
More discussion on the topic can be found here and some documentation on the topic can be found here.
Even though I am now able to do a POST to my rails app I am still getting a Error 500 in my Android Studio log that looks like this:
{"error":"Internal Server Error","status":"500"}
It doesn't seem to be hindering functionality right now so I am not going to lose any sleep over it.

Categories

Resources