I am having trouble with sending JSON data from my Android application written in C# using Xamarin Android (MvvmCross).
The function in Android application could run with no exception; however, my web service (written in Node JS using Express) seems not detecting the post request on its endpoint. Note that the other endpoints which use get (to send the data from web service to Android app) are working perfectly.
Below is my function to post my data to my web service
public async Task<int> insertSales(IEnumerable<Models.SalesTable> newsales)
{
/*ServerDatabaseApi.insertSalesEndpoint = "http://" + ipAddress + ":" + port +
"/insertsales";*/
WebRequest request = WebRequest.CreateHttp(ServerDatabaseApi.insertSalesEndpoint);
request.Method = "POST";
request.ContentType = "application/json";
try
{
using (var streamwriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
string json = JsonConvert.SerializeObject(newsales, Formatting.Indented);
streamwriter.Write(json);
streamwriter.Flush();
}
return 1;
}
catch (WebException we)
{
return 0;
}
}
When running the function above, it is always succeed (return 1; always executes). I have also tried checking the JSON serialization and it is working perfectly fine.
Below also attached the endpoint code used to serve the data.
/*endpoint for inserting a new sales to sales table*/
app.post('/insertsales', function(appReq, appRes){
console.log("Insert sales; count : "+ appReq.body.length);
sql.connect(conn).then(function(){
console.log("Insert sales; count : "+ appReq.body.length);
for (var i = 0 ; i < appReq.body.length ; i++) {
new sql.Request()
.query("insert into SalesTable " +
"values ('"+appReq.body[i].DocumentNo+"','"+appReq.body[i].DateCreated+"','"+appReq.body[i].Location+"',"+
appReq.body[i].TotalDiscountAmount+","+appReq.body[i].Total+",'"+appReq.body[i].SalesmanId+"','"+
appReq.body[i].CustomerId+"',"+appReq.body[i].Latitude+","+appReq.body[i].Longitude+")")
.catch(function(err){
console.log(err);
});
}
}).catch(function(err){
console.log(err);
});
});
I tried to trace whether it reached the endpoint or not using console.log. However, it never executes.
Could you help me to spot where I went wrong? Thanks in advance.
There's nothing in your .NET code that actually sends the WebRequest. You create the request, write some JSON to it's stream, and flush it. Here's a simple way to make the network call (untested):
public async Task<int> InsertSales(IEnumerable<Models.SalesTable> newSales)
{
var ipAddress = "";// your IP address here
var port = ""; // your port here
var endpoint = $"http://{ipAddress}:{port}/insertsalesline";
var requestString = JsonConvert.SerializeObject(newSales);
var content = new StringContent(requestString, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
{
var reponse = await client.PostAsync(endpoint, content);
if (reponse.IsSuccessStatusCode)
return 1;
else
return 0;
}
}
Related
I'm creating a messenger app, currently working on the client side, and I already have programmed most of the API (server side).
I am trying to send a POST request to the server, but it places \r\n after every element of the packet (eg:-, content-type header = application/json\r\n)
This means that when my server attempts to validate whether the incoming traffic is in JSON format, it throws it off.
I have used WireShark to see what the packets look like, here's a screenshot:
The code that I have used to send the request:
using Newtonsoft.Json;
using System.Net.Http;
public async static Task<HttpResponseMessage> PostRequestAsync(string path, object data)
{
var request = new HttpRequestMessage
{
RequestUri = new Uri(Main.ServerAddress + ":" + Main.ServerPort.ToString() + path),
Method = HttpMethod.Post,
};
MessageBox.Show("Content: " + JsonConvert.SerializeObject(data));
request.Content = new StringContent(JsonConvert.SerializeObject(data));
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
MessageBox.Show(request.ToString());
var response = new HttpResponseMessage();
using (HttpClient client = new HttpClient())
{
response = await client.SendAsync(request);
}
return response;
}
The object that is being serialised as json / sent to server:
public class UserCreds{
public string username;
public string password;
}
Thanks in advance.
EDIT
Here's the server code:
#server.route("/1/signup", methods=["POST"]) # Sign up a user.
def sign_up_user():
if request.is_json == False:
return make_response(), 400
userJson = request.get_json()
uName = userJson['username']
pWord = userJson['password']
#try:
file = open(userCredsFile, 'r')
fileRaw = file.read()
creds = {}
if fileRaw != "":
creds = json.loads(fileRaw)
if uName in creds:
return create_status_response("User already exists.")
else:
creds[uName] = pWord
file.close()
file = open(userCredsFile, 'w')
file.write(json.dumps(creds))
file.close();
return make_response(), 201
#except e:
# return create_status_response("Something happened.")
I need to send this HTTP Post Request:
POST https://webapi.com/baseurl/login
Content-Type: application/json
{"Password":"password",
"AppVersion":"1",
"AppComments":"",
"UserName":"username",
"AppKey":"dakey"
}
It works great in RestClient and PostMan just like above.
I need to have this pro-grammatically and am not sure if to use
WebClient, HTTPRequest or WebRequest to accomplish this.
The problem is how to format the Body Content and send it above with the request.
Here is where I am with example code for WebClient...
private static void Main(string[] args)
{
RunPostAsync();
}
static HttpClient client = new HttpClient();
private static void RunPostAsync(){
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
Inputs inputs = new Inputs();
inputs.Password = "pw";
inputs.AppVersion = "apv";
inputs.AppComments = "apc";
inputs.UserName = "user";
inputs.AppKey = "apk";
var res = client.PostAsync("https://baseuriplus", new StringContent(JsonConvert.SerializeObject(inputs)));
try
{
res.Result.EnsureSuccessStatusCode();
Console.WriteLine("Response " + res.Result.Content.ReadAsStringAsync().Result + Environment.NewLine);
}
catch (Exception ex)
{
Console.WriteLine("Error " + res + " Error " +
ex.ToString());
}
Console.WriteLine("Response: {0}", result);
}
public class Inputs
{
public string Password;
public string AppVersion;
public string AppComments;
public string UserName;
public string AppKey;
}
This DOES NOW WORK and responses with a (200) OK Server and Response
Why are you generating you own json?
Use JSONConvert from JsonNewtonsoft.
Your json object string values need " " quotes and ,
I'd use http client for Posting, not webclient.
using (var client = new HttpClient())
{
var res = client.PostAsync("YOUR URL",
new StringContent(JsonConvert.SerializeObject(
new { OBJECT DEF HERE },
Encoding.UTF8, "application/json")
);
try
{
res.Result.EnsureSuccessStatusCode();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
You are not properly serializing your values to JSON before sending. Instead of trying to build the string yourself, you should use a library like JSON.Net.
You could get the correct string doing something like this:
var message = JsonConvert.SerializeObject(new {Password = pw, AppVersion = apv, AppComments = acm, UserName = user, AppKey = apk});
Console.WriteLine(message); //Output: {"Password":"password","AppVersion":"10","AppComments":"","UserName":"username","AppKey":"dakey"}
var client = new RestClient("Your URL");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("apk-key", apk);
//Serialize to JSON body.
JObject jObjectbody = new JObject();
jObjectbody.Add("employeeName", data.name);
jObjectbody.Add("designation", data.designation);
request.AddParameter("application/json", jObjectbody, ParameterType.RequestBody);
try
{
var clientValue= client.Execute<Response>(request);
return RequestResponse<Response>.Create(ResponseCode.OK, "", clientValue.Data);
}
catch (Exception exception)
{
throw exception;
}
I made a tools to do it quick and easy:
Install-Package AdvancedRestHandler
or
dotnet add package AdvancedRestHandler
AdvancedRestHandler arh = new AdvancedRestHandler("https://webapi.com/baseurl");
var result = await arh.PostDataAsync<MyLoginResponse, MyLoginRequest>("/login", new MyLoginRequest{
Password = "password",
AppVersion = "1",
AppComments = "",
UserName = "username",
AppKey = "dakey"
});
public class MyLoginRequest{
public string Password{get;set;}
public string AppVersion{get;set;}
public string AppComments{get;set;}
public string UserName{get;set;}
public string AppKey{get;set;}
}
public class MyLoginResponse {
public string Token{get;set;}
}
Extra:
One other thing you can do is to use ArhResponse:
Either this way, in the class definition:
public class MyLoginResponse: ArhResponse
{
...
}
Or this way, in the API call:
var result = await arh.PostDataAsync<ArhResponse<MyLoginResponse>, MyLoginRequest> (...)
and instead of try or cache, check your API call state using simple if statements:
// check service response status:
if(result.ResponseStatusCode == HttpStatusCode.OK) { /* api receive success response data */ }
// check Exceptions that may occur due to implementation change, or model errors
if(result.Exception!=null) { /* mostly serializer failed due to model mismatch */ }
// have a copy of request and response, in case the service provider need your request response and they think you are hand writing the service and believe you are wrong
_logger.Warning(result.ResponseText);
_logger.Warning(result.RequestText);
// Get deserialized verion of, one of the fallback models, in case the provider uses more than one type of data in same property of the model
var fallbackData = (MyFallbackResponse)result.FallbackModel;
Header Possible Issue
There are cases that the Server does not accept C# request due to the header that the HttpClient generates.
It is because HttpClient by default uses the value of application/json; charset=utf-8 for Content-Type...
For sending only application/json part as Content-Type and ignore the ; charset=utf-8 part, you can do as following:
For HttpClient you can fix it by looking into this thread: How do you set the Content-Type header for an HttpClient request?
As for (AdvancedRestHandler) ARH, I fixed it due to integration with some company, but I don't remember fully... I did it, either through options like of requests or through resetting the header value.
we will use HttpPost with HttpClient PostAsync for the issue.
using System.Net.Http;
static async Task<string> PostURI(Uri u, HttpContent c)
{
var response = string.Empty;
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.PostAsync(u, c);
if (result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString();
}
}
return response;
}
We will call it by creating a string that we will use to post:
Uri u = new Uri("http://localhost:31404/Api/Customers");
var payload = "{\"CustomerId\": 5,\"CustomerName\": \"Pepsi\"}";
HttpContent c = new StringContent(payload, Encoding.UTF8, "application/json");
var t = Task.Run(() => PostURI(u, c));
t.Wait();
Console.WriteLine(t.Result);
Console.ReadLine();
In my app, I insert the push notifications management. Now I'm testing only the Android area.
I sent the first test notifications with the FCM console. With this tool, if I send more than one notification, I see all of the in the notifications area of my phone (example: if I send 3 notifications with text "1", "2", and "3", I see all of them in the notification area).
Then, I tried to write some C# code to send these notifications from my server. I can now send the notifications from my code, but if I make the same test as above, I see only the last notification ("3") and not all of them.
I'm sure there is some parameter to be set, but I can't figure out which one is.
Do you know where have I to fix something? Below my C# code:
public string SendMessage()
{
string serverKey = "myserverkey";
try
{
var result = "-1";
var webAddr = "https://fcm.googleapis.com/fcm/send";
var regID = "myAndroidPhoneID";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("Authorization:key=" + serverKey);
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"to\": \"" + regID + "\",\"data\": {\"message\": \"1This is a Firebase Cloud Messaging Topic Message!\",},\"priority\":10}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
result = streamReader.ReadToEnd();
}
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return "err";
}
}
I also tried to add in my json the parameter collapse_key, changing its value every notification, but I see always only the last.
Solved using this json instead of the posted one:
string json = "{\"to\": \"" + regID + "\",\"notification\": {\"title\": \"New deal\",\"body\": \"20% deal!\"},\"priority\":10}";
Summarized all the steps here: https://programmingistheway.wordpress.com/2017/07/19/devextremephonegap-how-to-manage-push-notifications-with-fcm/
I have a site that exercises a third party API. This API will tell me the values of accounts for a user. The API has become unresponsive, and this has highlighted a flaw in my code. I am getting the exception "Thread was being aborted." in the try-catch trap around the call, and in EventViewer I can see the HttpException RequestTimedOut.
I am trying to use HttpClient, in a synchronous manner. This works fine when the API is responsive, but it is coming apart now that the API is very slow to respond.
I have an overview page that tries to gather all the account values for all users.
There are 3 types of users (CLIENT, LIAISON, MEMBER). The page makes three asynchronous Javascript calls for each type of account. The fact that there are three calls made is (most probably) a red-herring, but I am declaring it to be safe.
The server takes each call, gets the users for each call. So, for the CLIENT call, the system pulls up the all the CLIENT users, and serially steps through them. It makes an HTTP request per CLIENT user to get the account value.
Each call has a new HttpClient object created for it, wrapped in a using statement.
I strongly suspect that IIS is spotting the idle thread and shutting it down, which is fair enough.
How can I make the API call so that I return the thread to the pool until I get a response?
private static string getTotalAssets(string blockChainAddress, string identity)
{
var prefix = "getTotalAssets() - ";
string msg = "";
using (var handler = new HttpClientHandler { UseCookies = false, PreAuthenticate = true })
using (var client = new HttpClient(handler))
{
// Timeout
TimeSpan tsTimeout = new TimeSpan(0, 0, 0, _timeout);
client.Timeout = tsTimeout;
var message = new HttpRequestMessage(HttpMethod.Post, _urlSettleAPIGetTotalAssets);
// Accept Json
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants.MIME_JSON));
// Form the content to send
string content = getTotalAssetsContent(blockChainAddress);
// Add to the message
message.Content = new StringContent(content);
// Make API Call
string sCallRef = "GET TOTAL ASSETS FOR [" + identity + "]";
Tuple<HttpResponseMessage, string> result = MakeAPICall(client, message, content, sCallRef);
if (result.Item1 == null)
return result.Item2; // Error during call
// Call returned
HttpResponseMessage response = result.Item1;
string responseString = result.Item2;
if (!response.IsSuccessStatusCode)
{
msg = "Response Status Code: " + response.StatusCode.ToString();
logger.Debug(prefix + msg);
return "Error [" + result.Item2 + "]; " + msg;
}
// Else, call returned 200-OK
logger.Debug(prefix + "Response:\r\n" + responseString + "\r\n");
return processGetTotalAssetsResponseString(responseString);
} // end of using...
}
public static Tuple<HttpResponseMessage, string> MakeAPICall(HttpClient client, HttpRequestMessage message, string content, string callRef)
{
var prefix = "MakeAPICall() - ";
string msg = "";
HttpResponseMessage responseMessage;
string responseString = "";
try
{
responseMessage = client.SendAsync(message).GetAwaiter().GetResult();
responseString = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return new Tuple<HttpResponseMessage, string>(responseMessage, responseString);
}
catch (Exception ex)
{
msg = "Exception during call; \r\nBaseMessage:[" + ex.GetBaseException().Message + "]; \r\nMessage:[" + ex.Message + "]";
logger.Warn(prefix + msg);
return new Tuple<HttpResponseMessage, string>(null, msg);
}
} // end of MakeAPICall()
I am attempting to connect to Cloudant (a couch-style DB) from a .Net MVC application. I am following the guidelines for consuming a web API using the HttpClient, as illustrated here:
http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client
I have two methods so far -- one to get a document and one to create a document -- and both have errors. The Get method returns Unauthorized and the Post method returns MethodNotAllowed.
The client is created like this:
private HttpClient CreateLdstnCouchClient()
{
// TODO: Consider using WebRequestHandler to set properties
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(_couchUrl);
// Accept JSON
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
The Get method is:
public override string GetDocumentJson(string id)
{
string url = "/" + id;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
strContent = response.Content.ReadAsStringAsync().Result;
}
else
{
// DEBUG
strContent = response.StatusCode.ToString();
LslTrace.Write("Failed to get data from couch");
}
}
return strContent;
}
The Post method is:
public override string CreateDocument(object serializableObject)
{
string url = CouchApi.CREATE_DOCUMENT_POST;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.PostAsJsonAsync(url, serializableObject).Result;
strContent = response.Content.ReadAsStringAsync().Result;
}
if (response.IsSuccessStatusCode)
{
return strContent;
}
else
{
LslTrace.Write("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return response.StatusCode.ToString();
}
}
URLs are per the API documentation: https://username:password#username.cloudant.com.
I am very confused by what is going on and having a lot of trouble finding examples. Thanks for your help!
Thomas
With the HttpClient, you need to do the following to authenticate correctly (assuming you use basic auth):
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential(_userName, _password);
HttpClient client = new HttpClient(handler) {
BaseAddress = new Uri(_couchUrl)
};
You should not specify the username/password in the _couchUrl - HttpClient doesn't support that.
I can't see your implementation of PostAsJsonAsync or the complete Url your are building, but you can try inspecting / logging response.ReasonPhrase when an error occurs to get a hint as to what went wrong.