I want to send a http post that can take a couple of seconds to reply without freezing my UI, currently this code just hangs my application when the method is callled.
What am I doing wrong and how do i achieve my goal?
private async Task<string> DoHttpClientPost(string method, IDictionary<string, object> args = null)
{
{
HttpClientHandler handler = new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
handler.Proxy = null;
HttpResponseMessage response;
using (var myHttpClient = new HttpClient(handler))
{
myHttpClient.DefaultRequestHeaders.ExpectContinue = false;
myHttpClient.DefaultRequestHeaders.Add("Accept-Charset", "ISO-8859-1,utf-8");
myHttpClient.DefaultRequestHeaders.Add(APPKEY_HEADER, CustomHeaders.GetValues(APPKEY_HEADER));
myHttpClient.DefaultRequestHeaders.Add(SESSION_TOKEN_HEADER, CustomHeaders.GetValues(SESSION_TOKEN_HEADER));
myHttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json-rpc"));
var call = new JsonRequest { Method = method, Id = 1, Params = args };
var jsonObject = JsonConvert.Serialize<JsonRequest>(call);
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json-rpc");
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
}
Console.WriteLine("\nCalling: " + method + " With args: " + JsonConvert.Serialize<IDictionary<string, object>>(args));
string jsonResponse = await response.Content.ReadAsStringAsync();
return jsonResponse;
}
}
public T Invoke<T>(string method, IDictionary<string, object> args = null)
{
if (method == null)
throw new ArgumentNullException("method");
if (method.Length == 0)
throw new ArgumentException(null, "method");
var jsonString = DoHttpClientPost(method, args).Result;
var jsonResult = JsonConvert.Deserialize<JsonResponse<T>>(jsonString);
return jsonResult.Result;
}
var jsonString = DoHttpClientPost(method, args).Result;
This is your culprit. If you call .Result on a Task from the UI thread it will hang.
You'll need to async all the way up - so Invoke should be async and return a Task<T> and await the DoHttpClientPost call, the caller should be async etc. etc. etc.
You have to make two changes
Modify this line from
response = await myHttpClient.PostAsync(new Uri(EndPoint), content);
to
response = await myHttpClient.PostAsync(new Uri(EndPoint), content).ConfigureAwait(false);
And looks like your intention is to wait for the post call to complete and return the results, so modify this line from
var jsonString = DoHttpClientPost(method, args).Result;
to
var jsonStringTask = DoHttpClientPost(method, args);
jsonStringTask.Wait(); //wait for http post call to complete.
var jsonString = jsonStringTask.Result;
Related
I'm creating a Task in C# but I'm not sure what I do is correct. I'm using Restsharp and in Restsharp there are two methods: Execute and ExecuteAsync. I want to do an Async call but I also need to return data to the client without blocking the execution.
Therefore I created a task which will use Execute instead of ExecuteAsync. The reason why is because I have to wait until I get a response back and then return it in the right data structure. So I thought there is no use in using ExecuteAsync if I have to await it in a Task...
My code looks as follows:
public Task<Response> ExecuteAsync()
{
return new Task<Response>(() =>
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = client.Execute(request);
return new Response()
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
});
}
Is this correct? The client should be able to call ExecuteAsync without blocking the execution.
I strongly suspect you should really just use ExecuteAsync and write an async method:
public async Task<Response> ExecuteAsync()
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = await client.ExecuteAsync(request).ConfigureAwait(false);
return new Response
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
}
I am trying to send an empty body to a Post Request but it does not execute.
I have already tried this:
Post an empty body to REST API via HttpClient
static async Task<string> CancelSale(string mainUrl, string bearerInfo,string systemNumber)
{
var cancelsaleUrl = mainUrl + $"api/sale/cancel/{systemNumber}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerInfo);
var data = new StringContent(null, Encoding.UTF8, "application/json");
var saleResponse = await client.PostAsync(cancelsaleUrl, data);
var responseBody = await saleResponse.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
return responseBody;
}
But it just does not execute, no exception.
I also tried this :
var saleResponse = await client.PostAsync(cancelsaleUrl, null);
which also does the same result.
Any ideas?
The problem was very simple. I had the result of whole this method in a variable and It did not await the method:
var cancelSale = CancelSale(mainUrl, bearerInfo, systemNumber);
Once it reaches anything that awaits it stops and leaves the method.
Here is the working code:
var cancelSale = await CancelSale(mainUrl, bearerInfo, systemNumber);
static async Task<string> CancelSale(string mainUrl, string bearerInfo,string systemNumber)
{
var cancelsaleUrl = mainUrl + $"api/sale/cancel/{systemNumber}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerInfo);
var saleResponse = await client.PostAsync(cancelsaleUrl, null);
var responseBody = await saleResponse.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
return responseBody;
}
I am attempting to send POST data to my server and get back a response. For some reason, no POST data is actually getting sent. A request is being sent to my server but the POST array is empty.
Here is my code for sending the request:
public class GlobalMethods
{
public async Task<string> callAjax(string mthd,NameValueCollection parameters)
{
var client = new HttpClient();
var content = JsonConvert.SerializeObject(parameters);
var result = await client.PostAsync("http://dev.adex-intl.com/adex/mvc/receiving/"+mthd, new StringContent(content)).ConfigureAwait(false);
var tokenJson = "";
if (result.IsSuccessStatusCode)
{
tokenJson = await result.Content.ReadAsStringAsync();
}
return tokenJson;
}
}
And here is my code that calls the above method:
public void loginPressed(object sender, EventArgs e)
{
if(String.IsNullOrEmpty(badge.Text)) {
DisplayAlert("Error", "Enter your badge number", "Ok");
} else {
IsBusy = true;
NameValueCollection parameters = new NameValueCollection();
parameters["badgetNumber"] = badge.Text;
GlobalMethods globalMethods = new GlobalMethods();
var results = globalMethods.callAjax("login", parameters);
}
}
I'm not sure what I'm doing wrong. Also, I'm a newbie to Xamarin and C# so I'm not even sure if the way I am attempting to do things is the best way.
You haven't specify the type of content that you want to send, in your case it's 'application/json', you can set it like that:
"var client = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(parameters));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");". Also, I would suggest you to write code like that:
var uri = new Uri(url);
using (var body = new StringContent(JsonConvert.SerializeObject(data)))
{
body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var request = new HttpRequestMessage
{
Version = new Version(1, 0),
Content = body,
Method = HttpMethod.Post,
RequestUri = uri
};
try
{
using (var response = await _client.SendAsync(request,cancellationToken))
{
if (response.IsSuccessStatusCode)
{
//Deal with success response
}
else
{
//Deal with non-success response
}
}
}
catch(Exception ex)
{
//Deal with exception.
}
}
You can use PostAsync for async sending data to server. your code should be something like this:
HttpClient client = new HttpClient();
var values = new Dictionary<string, string>
{
{ "p1", "data1" },
{ "p2", "data2" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://www.example.com/index.php", content);
var responseString = await response.Content.ReadAsStringAsync();
I have the following code:
//ids are a list of strings
foreach (string id in ids)
{
string serviceurl = "https://xxx.xx.xxx/internal/v2/idsearch?id=" + id;
List<string> lst = new List<string>();
var task = WebUtil.MakeAsyncRequest("GET", serviceurl, "application/json");
string val = task.Result;
if (val != "")
lst.Add(val);
}
public static Task<string> MakeAsyncRequest(string method,string url, string contentType)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = contentType;
request.Method = method;
request.Timeout = 20000;
request.Proxy = null;
string clientId = ConfigManager.GetClientId;
string clientSecret = ConfigManager.GetClientSecret;
request.Headers.Add(HttpRequestHeader.Authorization, ConfigManager.GetServiceKey);
request.Headers.Add("client_id", clientId);
request.Headers.Add("client_secret", clientSecret);
Task<WebResponse> task = Task.Factory.FromAsync(
request.BeginGetResponse,
asyncResult => request.EndGetResponse(asyncResult),
(object)null);
return task.ContinueWith(t => ReadStreamFromResponse(t.Result));
}
private static string ReadStreamFromResponse(WebResponse response)
{
using (Stream responseStream = response.GetResponseStream())
using (StreamReader sr = new StreamReader(responseStream))
{
//Need to return this response
string strContent = sr.ReadToEnd();
return strContent;
}
}
It all works fine but i am just not sure if i am implementing the async process properly as it seems like it is doing it synchronously. I would really appreciate anyone who could help me identify what i am doing wrong...thanks!
EDIT:
Here is how i solved it....
var taskList = new List<Task<string>>();
foreach (string id in ids)
{
string serviceurl = "https://xxx.xx.xxx/internal/v2/idsearch?id=" + id;
taskList.Add(WebUtil.MakeAsyncRequest(serviceurl, "application/json"));
}
try
{
string[] val = await Task.WhenAll(taskList.ToArray());
}
catch (Exception)
{
throw;
}
public static async Task<string> MakeAsyncRequest(string url, string contentType)
{
using (HttpClient client = new HttpClient())
{
var requestMessage = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = HttpMethod.Get,
};
// Add our custom headers
requestMessage.Headers.Add("client_id", ConfigManager.GetClientId);
requestMessage.Headers.Add("client_secret", ConfigManager.GetClientSecret);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(ConfigManager.GetServiceKey);
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));
HttpResponseMessage response = await client.SendAsync(requestMessage);
var result = await response.Content.ReadAsStringAsync();
string res = result.ToString();
return res;
}
}
ReadToEnd is synchronous, yes. There are async equivalents.
Also note, that DNS resolution is synchronous here. This is a bug that is persistently not being fixed.
Probably, the best fix is to use HttpClient which makes downloading a string asynchronously a one-liner.
I suppose the best way for making an async request is HttpClient.
You can write something like this:
var client = new HttpClient();
var request = new HttpRequestMessage()
{
RequestUri = new Uri("xxxxxx"),
Method = HttpMethod.Get,
};
request.Headers.Accept.Add(new ....);
var response = await client.SendAsync(request);
//read a result from the repsonse
I'm using RestSharp to do a GET request Asynchronously but it never ends. If I do the same request Synchronously it ends successfully.
I have used RestSharp just like this before and it always worked.
What am I doing wrong?
Here's the async method: (this.Client is just localhost)
public async Task<ServiceResponse> UpvoteArticleAsync(string source, string url)
{
var client = this.Client;
var request = new RestRequest("service/upvote/"+source+"/"+url, Method.GET) { RequestFormat = DataFormat.Json };
var cancellationTokenSource = new CancellationTokenSource();
var deserial = new JsonDeserializer();
var response = await client.ExecuteTaskAsync(request, cancellationTokenSource.Token);
return deserial.Deserialize<ServiceResponse>(response);
}
and this is how I call it:
ServiceResponse result = await rest.UpvoteArticleAsync (this.article.Source, this.article.Url);
As I said if I change the async method to a sync one, it works:
public async Task<ServiceResponse> UpvoteArticleAsync(string source, string url)
{
var client = this.Client;
var request = new RestRequest("service/upvote/"+source+"/"+url, Method.GET) { RequestFormat = DataFormat.Json };
var cancellationTokenSource = new CancellationTokenSource();
var deserial = new JsonDeserializer();
var response = client.ExecuteTaskAsync(request, cancellationTokenSource.Token);
return deserial.Deserialize<ServiceResponse>(response.Result);
}
EDIT:
I used try and catch to see if there's any exceptions but it didn't throw any.
EDIT 2:
Even if I do the same request with HttpWebRequest from System.Net it still gets stuck when using the async version of GetResponse:
public async Task<bool> UpvoteArticleAsync(string source, string url)
{
var request = HttpWebRequest.Create(string.Format(#"http://192.168.43.199:8080/service/upvote/{0}/{1}", source, url));
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();
if(string.IsNullOrWhiteSpace(content)) {
Console.Out.WriteLine("Response contained empty body...");
}
else {
Console.Out.WriteLine("Response Body: \r\n {0}", content);
}
}
}
return true;
}