UWP 10 C# HttpResponseMessage task freezes - c#

I couldn't find from search anyone having similar issues so:
I'm trying to get XML from server with HttpClient, but my UI freezes weirdly at line "task.Wait()". This is the code:
public void register() {
String data = "register=" + (accountName) + "&email0=" +
(email) + "&email1=" + (otherEmail);
var task = MakeRequest(data);
task.Wait(); //freezes here
var response = task.Result;
String resultstring = response.Content.ReadAsStringAsync().Result;
}
private static async Task<System.Net.Http.HttpResponseMessage> MakeRequest(String data)
{
var content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage responseMessage=null;
try
{
responseMessage = await httpClient.PostAsync(server, content);
}
catch(Exception ex)
{
responseMessage.ReasonPhrase = string.Format("RestHttpClient.SendRequest failed: {0}", ex);
}
return responseMessage;
}
Any help is greatly appreciated!

It's not freezing weirdly at all - it's freezing entirely reasonably.
You're calling task.Wait(), which stops your UI thread from doing any more work until that task has completed. However, that task itself needs to get back to the UI thread when the task returned by httpClient.PostAsync completes, in order to continue with the rest of your async method.
Basically, you shouldn't use Task.Wait() or Task.Result unless you know for sure that the task itself won't be blocked waiting to continue on the thread you're currently executing on.
Ideally, your register method (which should be Register to follow .NET naming conventions) should be asynchronous as well, so you can await the task returned by MakeRequest.
Additionally, you should probably change the way MakeRequest awaits the task - as the rest of that method doesn't really need to run on the UI thread, you can use:
responseMessage = await httpClient.PostAsync(server, content).ConfigureAwait(false);
Finally, this line:
responseMessage.ReasonPhrase = string.Format("RestHttpClient.SendRequest failed: {0}", ex);
... will throw a NullReferenceException if it ever executes. If an exception is thrown, responseMessage will still be null.

Answer of Jon Skeet solved my problem, and here is the working code, if some other beginner is having same problems.
public async void Register{
String data = "register=" + (accountName) + "&email0=" +
(email) + "&email1=" + (otherEmail);
var task = await MakeRequest(data);
String resultstring = taskContent.ReadAsStringAsync().Result;
}
private static async Task<System.Net.Http.HttpResponseMessage> MakeRequest(String data)
{
var content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");
var httpClient = new System.Net.Http.HttpClient();
return await httpClient.PostAsync(server, content).ConfigureAwait(false);
}
Thank you very much!

Related

Getting a token from an external web API is returning system.threading.tasks

I'm successfully getting a token back from my GetAccessToken() and GetAccessTokenAsync methods, but the token isn't retrieved until after the main method of GetCourses, which won't work because that's the method that collects the data I need to show on my cshtml page. I've tried pulling apart this controller and creating a Globals class that will house just the URIs, apiKey, and token, but then I read that's bad practice for MVC so I ditched that effort. It was getting called after the GetCourses method anyway, so it was dead end too.
I'm newer to MVC and come from a WebForms background where I was used to being able to throw this kind of code in my PageInit, but am struggling to figure out how to pull this off in MVC. Can someone help me figure out what I am doing wrong or if I need to go about this a different way?
public ActionResult GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = GetAccessToken().ToString();
IEnumerable<Course> courses = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri((string)TempData["EthosURI"]);
client.DefaultRequestHeaders.Add("Authorization", "Bearer {" + (string)TempData["Token"] + "}");
client.DefaultRequestHeaders.Add("Accept", "application/json");
//HTTP GET
var responseTask = client.GetAsync("courses");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
}
else //web api sent error response
{
//log response status here..
courses = Enumerable.Empty<Course>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(courses);
}
public static async Task<string> GetAccessToken()
{
var token = await GetAccessTokenAsync("redactedUrl", "redactedAPIKey");
return token;
}
public static async Task<string> GetAccessTokenAsync(string ethosURI, string apiKey)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ethosURI);
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(ethosURI)
};
request.Headers.Clear();
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("Accept", "application/json");
request.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true };
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
The (non-blocking) way in C# to wait for a task to complete is to use the await keyword. And for a method to use the await keyword, it has to be marked async. By using await, you not only wait for the task to complete, but also the current thread is not blocked. Wrapping an asynchronous operation in another method would not make it synchronous. In other words, the asynchronous nature propagates up the call hierarchy and the caller has to await. So, the GetAccessToken() still has to be awaited. A controller action can be marked asynchronous as well, so you probably want:
public async Task<ActionResult> GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = (await GetAccessToken()).ToString(); // note the additional parentheses
....
Note the additional parantheses above before calling ToString(). However, since GetAccessToken() already returns a string, you don't need the redundant ToString() call:
TempData["Token"] = await GetAccessToken();
Now, you can also change this:
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
to just:
courses = await result.Content.ReadAsAsync<IList<Course>>();
Microsoft has quite good documentation on asynchronous programming and I would recommend checking it out.
That's not how async works in C#. You need either to make GetCourses() async AND await for GetAccessToken(), or use dirty hack GetAccessToken().GetAwaiter().GetResult() but it may become not safe in certain circumstances.

Issue with HttpClient.PostAsJsonAsync Not Working from ASPX

I have created an ASP.NET Core 2.1 service and I can call it just fine from a console application. However, when I use the very same code to call it from an ASPX page, it does not return an answer. It just never goes past _client.PostAsJsonAsync and seems to run forever. It should only take a handful of seconds to go through that line. Any idea on what I am missing?
List<OutputAddress> outputAddresses = RunAsync(inputAddresses).GetAwaiter().GetResult();
static async Task<List<OutputAddress>> RunAsync(List<InputAddress> addresses)
{
// Update port # in the following line.
_client.BaseAddress = new Uri("http://<servername>/GeocodeAPI/");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_client.Timeout = new TimeSpan(0, 10, 0);
try
{
HttpResponseMessage response = await _client.PostAsJsonAsync("api/Geocode/Addresses", addresses);
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<List<OutputAddress>>(result);
}
else
return null;
}
catch (Exception e)
{
return null;
}
}
=======================
Huge thank you to Nkosi for their response. Here's what I had to change:
Function calling ASP.NET Core service
static async Task<List<OutputAddress>> RunAsync(List<InputAddress> addresses)
{
_client.BaseAddress = new Uri("http://<servername>/GeocodeAPI/");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_client.Timeout = new TimeSpan(0, 10, 0);
try
{
HttpResponseMessage response = await _client.PostAsJsonAsync("api/Geocode/Addresses", addresses);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<OutputAddress>>(result);
}
else
return null;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}
Function calling the above function (RunAsync): added async keyword
private async void ReadCsvFile(string filepath)
{
...
List<OutputAddress> outputAddresses = await RunAsync(inputAddresses);
...
}
Added Async="true" to aspx code:
<%# Page ... Async="true" %>
Mixing async-await and blocking calls like .Result; can cause deadlocks
await the calls to getting the content
var result = await response.Content.ReadAsStringAsync();
Also, if using async-await, go async all the way.
List<OutputAddress> outputAddresses = await RunAsync(inputAddresses);
Reference Async/Await - Best Practices in Asynchronous Programming
Beware of how you're using asynchronous code in ASP.NET Web Forms.
You need to use page async tasks.
That being said, nothing in async changes the HTTP protocol and, if you want some behavior on the client side, you need to implement it on the client, as Anu showed.

How to post data using HttpClient? (an answer than actually works)

There is another question about this, but it doesn't have a functioning solution at the end, and the only good answer, for some reason, doesn't work, not for the guy who ask it, not for me either.
This such question is here:
How to post data using HttpClient?
Given the corresponding aclarations, this is the code I have so far:
The methods to call the method who connects with the web server:
private void Button_Click(object sender, System.EventArgs e)
{
//. . . DO SOMETHING . . .
PopulateListView();
//. . . DO SOMETHING ELSE . . .
}
private void PopulateListView()
{
//. . . DO SOMETHING . . .
list = await "http://web.server.url".GetRequest<List<User>>();
//. . . DO SOMETHING ELSE . . .
}
The method than connects with the web server:
public async static Task<T> SendGetRequest<T>(this string url)
{
try
{
var uri = new Uri(url);
HttpClient client = new HttpClient();
//Preparing to have something to read
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("OperationType", "eaf7d94356e7fd39935547f6f15e1c4c234245e4")
});
HttpResponseMessage response = await client.PostAsync(uri, formContent);
#region - - Envio anterior (NO FUNCIONO, SIN USO) - -
//var stringContent = new StringContent("markString");
//var sending = await client.PostAsync(url, stringContent);
//MainActivity.ConsoleData = await client.PostAsync(url, stringContent);
#endregion
//Reading data
//var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
MainActivity.ConsoleData = json.ToString();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
}
catch(Exception ex)
{
Console.WriteLine("Error: "+ex.ToString());
return default(T);
}
}
You maybe guessed it, but I'm trying to make a method that send some data (through POST) called "markString" to a web-server than receive it and, depending of the "markString" it returns certain json Objects.
This web server is already working properly (I tested it out with some plug-in, it work like it should)
This method is supposed to send the "markString" and receive the data back so then i can use it in the app.
I'm making a Xamarin Android application.
Also have in mind than I don't have any connection problem at all, in fact the app is sending data in an excellent matter when I try to do it using web client, but I want it to send it using HttpClient.
The problem
The code is not returning anything. Any request for information, clarification, question, constructive comments or anything than can lead to an answer would be greatly appreciated too.
Thanks in advance.
Most deadlock scenarios with asynchronous code are due to blocking further up the call stack.
By default await captures a "context" (in this case, a UI context), and resumes executing in that context. So, if you call an async method and the block on the task (e.g., GetAwaiter().GetResult(), Wait(), or Result), then the UI thread is blocked, which prevents the async method from resuming and completing.
void Main()
{
var test = SendGetRequest("http://www.google.com");
test.Dump();
}
public async static Task<string> SendGetRequest(string url)
{
try
{
var uri = new Uri(url);
HttpClient client = new HttpClient();
//Preparing to have something to read
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("OperationType", "eaf7d94356e7fd39935547f6f15e1c4c234245e4")
});
HttpResponseMessage response = await client.PostAsync(uri, formContent);
#region - - Envio anterior (NO FUNCIONO, SIN USO) - -
//var stringContent = new StringContent("markString");
//var sending = await client.PostAsync(url, stringContent);
//MainActivity.ConsoleData = await client.PostAsync(url, stringContent);
#endregion
//Reading data
//var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return json;
}
catch (System.Exception ex)
{
Console.WriteLine("Error: " + ex.ToString());
return string.Empty;
}
}

HttpClient - A task was cancelled?

It works fine when have one or two tasks however throws an error "A task was cancelled" when we have more than one task listed.
List<Task> allTasks = new List<Task>();
allTasks.Add(....);
allTasks.Add(....);
Task.WaitAll(allTasks.ToArray(), configuration.CancellationToken);
private static Task<T> HttpClientSendAsync<T>(string url, object data, HttpMethod method, string contentType, CancellationToken token)
{
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method, url);
HttpClient httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(Constants.TimeOut);
if (data != null)
{
byte[] byteArray = Encoding.ASCII.GetBytes(Helper.ToJSON(data));
MemoryStream memoryStream = new MemoryStream(byteArray);
httpRequestMessage.Content = new StringContent(new StreamReader(memoryStream).ReadToEnd(), Encoding.UTF8, contentType);
}
return httpClient.SendAsync(httpRequestMessage).ContinueWith(task =>
{
var response = task.Result;
return response.Content.ReadAsStringAsync().ContinueWith(stringTask =>
{
var json = stringTask.Result;
return Helper.FromJSON<T>(json);
});
}).Unwrap();
}
There's 2 likely reasons that a TaskCanceledException would be thrown:
Something called Cancel() on the CancellationTokenSource associated with the cancellation token before the task completed.
The request timed out, i.e. didn't complete within the timespan you specified on HttpClient.Timeout.
My guess is it was a timeout. (If it was an explicit cancellation, you probably would have figured that out.) You can be more certain by inspecting the exception:
try
{
var response = task.Result;
}
catch (TaskCanceledException ex)
{
// Check ex.CancellationToken.IsCancellationRequested here.
// If false, it's pretty safe to assume it was a timeout.
}
I ran into this issue because my Main() method wasn't waiting for the task to complete before returning, so the Task<HttpResponseMessage> was being cancelled when my console program exited.
C# ≥ 7.1
You can make the main method asynchronous and await the task.
public static async Task Main(){
Task<HttpResponseMessage> myTask = sendRequest(); // however you create the Task
HttpResponseMessage response = await myTask;
// process the response
}
C# < 7.1
The solution was to call myTask.GetAwaiter().GetResult() in Main() (from this answer).
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);
The above is the best approach for waiting on a large request.
You are confused about 30 minutes; it's random time and you can give any time that you want.
In other words, request will not wait for 30 minutes if they get results before 30 minutes.
30 min means request processing time is 30 min.
When we occurred error "Task was cancelled", or large data request requirements.
Another possibility is that the result is not awaited on the client side. This can happen if any one method on the call stack does not use the await keyword to wait for the call to be completed.
Promoting #JobaDiniz's comment to an answer:
Do not do the obvious thing and dispose the HttpClient instance, even though the code "looks right":
async Task<HttpResponseMessage> Method() {
using (var client = new HttpClient())
return client.GetAsync(request);
}
Disposing the HttpClient instance can cause following HTTP requests started by other instances of HttpClient to be cancelled!
The same happens with C#'s new RIAA syntax; slightly less obvious:
async Task<HttpResponseMessage> Method() {
using var client = new HttpClient();
return client.GetAsync(request);
}
Instead, the correct approach is to cache a static instance of HttpClient for your app or library, and reuse it:
static HttpClient client = new HttpClient();
async Task<HttpResponseMessage> Method() {
return client.GetAsync(request);
}
(The Async() request methods are all thread safe.)
in my .net core 3.1 applications I am getting two problem where inner cause was timeout exception.
1, one is i am getting aggregate exception and in it's inner exception was timeout exception
2, other case was Task canceled exception
My solution is
catch (Exception ex)
{
if (ex.InnerException is TimeoutException)
{
ex = ex.InnerException;
}
else if (ex is TaskCanceledException)
{
if ((ex as TaskCanceledException).CancellationToken == null || (ex as TaskCanceledException).CancellationToken.IsCancellationRequested == false)
{
ex = new TimeoutException("Timeout occurred");
}
}
Logger.Fatal(string.Format("Exception at calling {0} :{1}", url, ex.Message), ex);
}
In my situation, the controller method was not made as async and the method called inside the controller method was async.
So I guess its important to use async/await all the way to top level to avoid issues like these.
I was using a simple call instead of async. As soon I added await and made method async it started working fine.
public async Task<T> ExecuteScalarAsync<T>(string query, object parameter = null, CommandType commandType = CommandType.Text) where T : IConvertible
{
using (IDbConnection db = new SqlConnection(_con))
{
return await db.ExecuteScalarAsync<T>(query, parameter, null, null, commandType);
}
}
Another reason can be that if you are running the service (API) and put a breakpoint in the service (and your code is stuck at some breakpoint (e.g Visual Studio solution is showing Debugging instead of Running)). and then hitting the API from the client code. So if the service code a paused on some breakpoint, you just hit F5 in VS.

Async await pattern help. Am I doing it right?

I have a "api" e.g. repository pattern, I wrote to return xml from the web and then hydrate it to classes.
It seems to hang on the result of the GetXmlAsync(url) method.
public async Task<string> GetXmlAsync(string url)
{
string xml = string.Empty;
HttpMessageHandler handler = new HttpClientHandler();
HttpClient httpClient = new HttpClient(handler);
Uri uri = new Uri(url, UriKind.Absolute);
HttpResponseMessage response = await httpClient.GetAsync(uri);
xml = await response.Content.ReadAsStringAsync();
return xml;
}
But when I use the same code in a unit test, it works.
In the app, I call it like so:
public async Task<IEnumerable<Post>> GetRecentAsync(int page)
{
string url = this.urls.GetRecent(page);
string xml = string.Empty;
var xmlTask = this.loader.GetXmlAsync(url);
xml = xmlTask.Result; // Hangs right here.
var results = this.modelLoader.XmlToPost(xml);
if (results.Count() < 1)
{
this.LastError = XmlLoadError;
}
return results.AsEnumerable();
}
[TestMethod]
public async Task Integration_HttpLoader_GetXmlAsync_GetRecent_Xml_ShouldNotBeNullOrEmpty()
{
int page = 1;
string xml = string.Empty;
IUrl url = this.GetUrlHelper();
ILoader loader = this.GetIntegrationLoader(false);
xml = await loader.GetXmlAsync(url.GetRecent(page));
Assert.IsTrue(!string.IsNullOrEmpty(xml));
}
In your app, you are not preceding the call to this.loader.GetXmlAsync(url) with await You hit this line, fire an async task on another thread then immediately proceed to the next line without having ever gotten the response. It works in your unit test because you correctly use the await keyword.
You are causing a deadlock by synchronously blocking on the result of the task.
Follow these best practices:
Do not block on async code (make it async all the way down).
e.g., var xml = await this.loader.GetXmlAsync(url); in GetRecentAsync.
Use ConfigureAwait(false) in your "library" async methods if they can continue on a thread pool thread.
e.g., var response = await httpClient.GetAsync(uri).ConfigureAwait(false); and xml = await response.Content.ReadAsStringAsync().ConfigureAwait(false); in GetXmlAsync.

Categories