Async/Await not working when calling the web API - c#

Hi I have this Asynchronous Web API, I think when I execute this in a button click, the rest of the processes won't continue until the API returns the results, Below is my API,
btnSubmit.Click += async (sender, e) =>
{
var jobId = txtJobID.Text;
if (txtJobID.Text.Length <= 0)
{
txtJobID.RequestFocus();
txtJobID.SetError("Job ID required", iconError);
}
else
{
var request = HttpWebRequest.Create(string.Format("http://192.168.79.174:90/api/test/" + jobId));
request.ContentType = "application/json";
request.Method = "GET";
using (HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
dynamic arr = JsonConvert.DeserializeObject(content);
foreach (dynamic obj in arr)
{
tvJobID.Text = obj.JobID;
tvJobType.Text = obj.JobType;
tvDueDate.Text = obj.DueDate;
tvVisitTime.Text = obj.Time;
tvVisitStatus.Text = obj.VisitStatus;
tvAddress1.Text = obj.Address1;
tvAddress2.Text = obj.Address2;
tvPostCode.Text = obj.PostCode;
tvAuthority.Text = obj.Authority;
}
if (content == null || content == "" || content == "[]")
{
//Console.Out.WriteLine("Response contained empty body...");
Toast.MakeText(ApplicationContext, "Invalid Job ID or no visits for this ID. Please try again", ToastLength.Long).Show();
}
else
{
layoutController.Visibility = ViewStates.Visible;
}
}
}
}
};
When I debug the code, Specially where the foreach statement is, it takes quite a bit of time for API to return results. What am I doing wrong? Thanks a lot for any help!

Related

Read more than 1000 rows from Azure Table Storage OData filter query?

How do we read more than 1000 rows from Azure Table Storage? Here is the code I'm using for reading data from Table Storage and this only retrieves 1000 rows:
readData()
{
var s = #$"https://{storageAccountName}.table.core.windows.net/{tableName}()";
var baseurl = $"{s}<sas-token>&$filter=Name%20eq%20'XYZ'";
var data = GetForOData(baseurl);
var responseData = data.Data.Replace(".", "_");
var odata = JsonConvert.DeserializeObject<ODataResponse>(responseData);
}
GetForOData(string url) {return InvokeForOData<Object>("GET", url, null, null);}
private static HttpResponseData InvokeForOData<T>(string method, string url, Object id, T data)
{
var Response = new HttpResponseData()
{
Code = HttpStatusCode.RequestTimeout, Data = string.Empty, Message = string.Empty
};
var PostParam = string.Empty;
if (data != null) { PostParam = data.ToString(); }
var postData = Encoding.UTF8.GetBytes(PostParam);
var request = (HttpWebRequest)WebRequest.Create(new Uri(url + (id == null ? "" : '/' + id.ToString())));
request.Method = method;
// add headers
if (postData.Length > 0)
{
using (Stream requestStream = request.GetRequestStream())
{ requestStream.Write(postData, 0, postData.Length); }
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Response.Code = response.StatusCode;
using (var stream = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{ Response.Data = stream.ReadToEnd(); }
}
return Response;
}
Where do I check for x-ms-continuation-NextPartitionKey and x-ms-continuation-NextRowKey and use them in the next request?
Update: I was able to find nextPartitionKey and nextRowKey header values. How do I pass these values in the next request?
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Response.Code = response.StatusCode;
var nextPartitionKey = response.Headers["x-ms-continuation-NextPartitionKey"];
var nextRowKey = response.Headers["x-ms-continuation-NextRowKey"];
using (StreamReader stream = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
Response.Data = stream.ReadToEnd();
}
}
A single call to Azure Table Storage will return a maximum of 1000 entities. If there are more entities matching a query, you will get a continuation token back and you will need to use that to get the next set of entities.
So in your case, in order to read more than 1000 entities you will have to send the request, get the data and check for continuation token in the response (x-ms-continuation-NextPartitionKey and x-ms-continuation-NextRowKey) and use them in the next request.
You can learn more about pagination in Azure Table Storage here: https://learn.microsoft.com/en-us/rest/api/storageservices/query-timeout-and-pagination.
UPDATE
Please see the code below (untested though):
private static HttpResponseData InvokeForOData<T>(string method, string url, Object id, T data)
{
var Response = new HttpResponseData()
{
Code = HttpStatusCode.RequestTimeout,
Data = string.Empty,
Message = string.Empty,
NextPartitionKey = string.Empty,
NextRowKey = string.Empty
};
var PostParam = string.Empty;
if (data != null) { PostParam = data.ToString(); }
var postData = Encoding.UTF8.GetBytes(PostParam);
var request = (HttpWebRequest)WebRequest.Create(new Uri(url + (id == null ? "" : '/' + id.ToString())));
request.Method = method;
// add headers
if (postData.Length > 0)
{
using (Stream requestStream = request.GetRequestStream())
{ requestStream.Write(postData, 0, postData.Length); }
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Response.Code = response.StatusCode;
var nextPartitionKey = response.Headers["x-ms-continuation-NextPartitionKey"];
var nextRowKey = response.Headers["x-ms-continuation-NextRowKey"];
using (StreamReader stream = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
Response.Data = stream.ReadToEnd();
Response.NextPartitionKey = nextPartitionKey;
Response.NextRowKey = nextRowKey;
}
}
return Response;
}
Basically what I have done is added two properties (NextPartitionKey and NextRowKey) in your HttpResponseData object and populate that with the nextPartitionKey and nextRowKey respectively you get from response header.
What you have to do in your code (where you're processing the response) is check if either of these two values are not null or empty. A non null/empty value would indicate that more entities are present.
If that's the case, then what you have to do is modify the URL by appending NextPartitionKey and NextRowKey values as query string parameters (please see the link above for details) and send the request again. You will need to do this till the time you get both of these values as either null or empty strings.

Open link in browser with HttpClient.GetAsync

Is it possible to open a link in browser with HttpClient.GetAsync?
I got a report from a user of my app that the app opened a link in the browser which is only used in with HttpClient.GetAsync.
Some one else had the issue too: why HttpClient.GetAsync causes opening link in browser?
private JObject Get(string url)
{
JObject getResponse = new JObject();
getResponse["error"] = (int)Error.None;
getResponse["message"] = "";
try
{
using (HttpClient client = new HttpClient())
{
Task<HttpResponseMessage> res1 = client.GetAsync(url + GetRequestRandomizer());
HttpResponseMessage res = res1.WaitAndUnwrapException();
if (res.IsSuccessStatusCode && res.StatusCode == HttpStatusCode.OK)
{
var responseContent = res.Content;
getResponse["error"] = (int)Error.None;
getResponse["message"] = responseContent.ReadAsStringAsync().Result;
}
else
{
getResponse["error"] = (int)Error.RequestError;
getResponse["message"] = "";
}
}
}
catch (HttpRequestException exception)
{
getResponse["error"] = (int)Error.NetworkError;
getResponse["message"] = "";
}
return getResponse;
}

How to add a specific user to a segment in OneSignal

I'm developing a C#.NET MVC Web Api for an Android application. At the moment I am using OneSignal to send push notifications to the users by calling the OneSignal Api and passing the notification content. I need to know how to add a user to a specific segment so that i can send notifications to individual users as well as users of that segment collectively. I have searched in on their documentation but I didn't understand how to do it using OneSignal.SendTag method. So basically how to do it in Visual Studio? So far i have done this:
string api_key = "dsabjd";
var request = WebRequest.Create("https://onesignal.com/api/v1/notifications") as HttpWebRequest;
if (user != null)
{
string message = "This job is posted by: \n" + user.Name + "\n" + user.Contact + "\n" +user.City;
if (request != null)
{
request.KeepAlive = true;
request.Method = "POST";
request.ContentType = "application/json";
request.Headers.Add("authorization", "Basic "+api_key);
var serializer = new JavaScriptSerializer();
var obj = new
{
app_id = "1651",
contents = new { en = message },
//data = new { image = "http://dsadasdasd.png" },
data = new { image = imageUrl },
included_segments = new string[] { "All" }
};
var param = serializer.Serialize(obj);
byte[] byteArray = Encoding.UTF8.GetBytes(param);
try
{
using (var writer = request.GetRequestStream())
{
writer.Write(byteArray, 0, byteArray.Length);
}
string responseContent=null;
using (var response = request.GetResponse() as HttpWebResponse)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
responseContent = reader.ReadToEnd();
}
}
if (responseContent != null)
{
// parsing the json returned by OneSignal Push API
dynamic json = JObject.Parse(responseContent);
int noOfRecipients = json.recipients;
if (noOfRecipients > 0)
{
flag = true;
}
}
}
catch (WebException ex)
{
flag = false;
}
}
}
To set tags it is recommend you use sendTags from the OneSignal Android SDK in your app it's supported offline and handles retries for you.
If you need to target individual users it is recommend to call idsAvailable in your app and send this to your server. You can later use the include_player_ids field on the create notification REST API POST call to send a notification to a list of users.

Google Cloud Messaging - Using "delay_while_idle" - App Server C#

First of all, I'm feeling bad asking this question because I think that the error it's something simple.
I'm using GCM on android and I wrote an app server in C#. Everything works fine but I have a doubt trying to specify delay_while_idle parameter. I put it in true but I doesn't work, I mean, if I have the device locked, the gcm messaging arrives immediately.
Here is my code
private string SendNotification( )
{
string result = string.Empty;
String GCM_URL = #"https://gcm-http.googleapis.com/gcm/send";
string collapseKey = DateTime.Now.ToString();
Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("data.title", HttpUtility.UrlEncode("title"));
data.Add("data.description", HttpUtility.UrlEncode("description"));
StringBuilder sb = new StringBuilder();
sb.AppendFormat("registration_id={0}&collapse_key={1}", REGISTRATION_ID, collapseKey);
sb.AppendFormat("&delay_while_idle=true");
foreach (string item in data.Keys)
{
if (item.Contains("data."))
sb.AppendFormat("&{0}={1}", item, data[item]);
}
string msg = sb.ToString();
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(GCM_URL);
req.Method = "POST";
req.Headers.Add("Authorization:key=" + API_KEY);
req.ContentType = "application/x-www-form-urlencoded;;charset=UTF-8";
req.ContentLength = msg.Length;
using (System.IO.StreamWriter oWriter = new System.IO.StreamWriter(req.GetRequestStream()))
{
oWriter.Write(msg);
}
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream()))
{
string respData = sr.ReadToEnd();
if (resp.StatusCode == HttpStatusCode.OK) // OK = 200
{
if (respData.StartsWith("id="))
{
result = "ok";
}
else
result = respData.ToString();
}
else if (resp.StatusCode == HttpStatusCode.InternalServerError || resp.StatusCode == HttpStatusCode.BadGateway ) // 500
result = "Internal server error";
else if (resp.StatusCode == HttpStatusCode.ServiceUnavailable || resp.StatusCode == HttpStatusCode.BadGateway ) // 503
result = "Server unavailable";
else if (resp.StatusCode == HttpStatusCode.Unauthorized) // 401
result = "invalid api key";
else
result = "Error: " + resp.StatusCode;
}
}
return result;
}
It's correct the way that I'm using the parameter "delay_while_idle"? (I tried with "delay_while_idle=1" but was the same)
Probably, I'm using a wrong format
[UPDATE]
Reading the GCM Documentation, I saw that I must put it in JSON format
The default value for delay_while_idle have to be false, and it must be a JSON boolean.

C# read from HttpResponseMessage

I am using .net's Httpclient for the first time and finding it very hard. I have managed to call the server and receive response from it but stuck at reading from the response. Here is my code:
if (Method == HttpVerb.POST)
response = client.PostAsync(domain, new StringContent(parameters)).Result;
else
response = client.GetAsync(domain).Result;
if (response != null)
{
var responseValue = string.Empty;
Task task = response.Content.ReadAsStreamAsync().ContinueWith(t =>
{
var stream = t.Result;
using (var reader = new StreamReader(stream))
{
responseValue = reader.ReadToEnd();
}
});
return responseValue;
}
responseValue has {} in it although the service is returning data. How should I fix the issue?
The project is in .Net 4.
You are creating an asynchronous task but not waiting for it to complete before returning. This means your responseValue never gets set.
To fix this, before your return do this:
task.Wait();
So your function now looks like this:
if (Method == HttpVerb.POST)
response = client.PostAsync(domain, new StringContent(parameters)).Result;
else
response = client.GetAsync(domain).Result;
if (response != null)
{
var responseValue = string.Empty;
Task task = response.Content.ReadAsStreamAsync().ContinueWith(t =>
{
var stream = t.Result;
using (var reader = new StreamReader(stream))
{
responseValue = reader.ReadToEnd();
}
});
task.Wait();
return responseValue;
}
If you prefer to use await (which you possibly should), then you need to make the function this code is contained in async. So this:
public string GetStuffFromSomewhere()
{
//Code above goes here
task.Wait();
}
Becomes:
public async string GetStuffFromSomewhere()
{
//Code above goes here
await ...
}
Try this
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(obj.Url);
HttpWebResponse response = null;
try
{
response = request.GetResponse() as HttpWebResponse;
}
catch (Exception ex)
{
}

Categories

Resources