Async method calling async method and awaiting another method (http client) - c#

I have some problem with async method.
public async void MakePost()
{
var cookieArray = GetCookies().Result;
(...)
}
async public Task<string[]> GetCookies()
{
(...)
var response = await httpClient.SendAsync(request);
string cookieTempSession = response.Headers.ToString();
(...)
return cookieArray;
}
Nothing happening after var response = await httpClient.SendAsync(request); I put breakpoint in next line string cookieTempSession = response.Headers.ToString(); but it never reach it. I tried to "try catch" but also nothing happend. When I merge this two methods into one it works perfect but it's not so pretty. I just wondering what happened there.

Since the first method is async, you should use await instead of Result:
var cookieArray = await GetCookies();
If you are not programming front end, add ConfigureAwait(false) (why?) to the call, like this:
var cookieArray = await GetCookies().ConfigureAwait(false);
...
var response = await httpClient.SendAsync(request).ConfigureAwait(false);

Related

C# HttpClient.PostAsync and await crashing app

I am quite new to C#. I'm using the await keyword to invoke HttpClient's API.
static async Task<HttpResponseMessage> CreateChannel(string channelName)
{
try
{
HttpClient client = new HttpClient();
var req = new
{
id= channelName
};
StringContent content = new StringContent(JsonConvert.SerializeObject(req).ToString(), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync("http://localhost:3000/channel", content);
response.EnsureSuccessStatusCode();
return response;
}
catch (Exception ex)
{
...
var view = new Dialog();
...
var result = await DialogHost.Show(view);
return null;
}
}
private void initSocketIo(string channel)
{
CreateChannel(channel).Wait();
...
// after this method we init UI etc.
}
I have 2 problems which I can't seem to be able to solve
After the client.PostAsync() method runs, my app just crashes. Simple as that. I understand it's because the main thread doesn't have anything else to process, but all the code above happens in the constructor code of MainWindow, so there is a lot more to do after the await
The exception is never triggered. How do I ensure that my exception catch clause is invoked in client.PostAsync() throws an exception.
Any code suggestion that just works would do :).
You are mixing blocking calls (.Result, .Wait()) with async calls which can lead to deadlocks.
Make initSocketTo async.
private async Task initSocketIo(string channel) {
var response = await CreateChannel(channel);
...
// after this method we init UI etc.
}
Also do not try to do async in the constructor. Move the heavier processes later in the life cycle. You could even raise an event and handle that on another thread so as not to block the flow.

ASP.Net Forms HttpClient PostAsync no response, no error message

I have this code:
using (var client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{"site_id","001"},
{"apikey","abc01201az1024"},
{"trans_id","45364136"},
};
// Get the parameters in the url encoded format
var content = new FormUrlEncodedContent(values);
//Send request
var response = await client.PostAsync(url, content);
DataRoot<Transaction> outPut = null;
if (response.IsSuccessStatusCode)
{
//Get Response
var result = await response.Content.ReadAsStringAsync();
JsonConvert.DeserializeObject<DataRoot<Transaction>>(result);
}
return outPut;
}
In the debug mode at this stage, the code does not produce any response, no error code but stops running:
//Send request
var response = await client.PostAsync(url, content);
there could be a better way but this solved my problem. Call your method from another one using wait:-
public static async Task<string> AuthenticateUser()
{
var t = Task.Run(() => ClassObject.AuthenticateUser("me"));
t.Wait();
Console.WriteLine(t.Result);
Console.ReadLine();
return "ok";
}
Using await like this, can end up in a deadlock.
You can use ConfigureAwait(false) in async methods for preventing such a deadlock.
Update code to:
var response = await client.PostAsync(url, content).ConfigureAwait(false);
This will solve the issue.
Are you sure that it isn't actually returning the response and continuing execution? Because the client.PostAsync() call is awaited execution may continue on a different thread. Therefore, if you're just debugging line by line (via F10 or similar) it may appear that the method never returns; in actuality the entire method has finished execution and your program is running.
You may need to add another breakpoint in the method (after the PostAsync method call). When the PostAsync method returns on a different thread, your debugger should hit the next breakpoint.

Await in async request webapi 2 client

I am trying to understand await an async operation in asp.net MVC web api 2 client (console application). I believe I have done it wrong (due to lack of understanding await and async). It doesn't seem to run async. Here is code to understand the problem
//Main function
static void Main()
{
RunAsync().Wait();
}
//RunAsync
static async Task RunAsync()
{
using (var client = new HttpClient())
{
var apiUrl = ConfigurationManager.AppSettings["apiurl"];
client.BaseAddress = new Uri(apiUrl);
....some code to fetch data
foreach (var updateObject in updatedata)
{
HttpResponse response = await client.PostAsJsonAsync("webapimethod", updateObject);
if (response.IsSuccessStatusCode)
{
JArray content = await response.Content.ReadAsAsync<JArray>();
}
}
}
}
In the above code, foreach loop I am making request in loop as PostAsJsonAsync call and then I use ReadAsAsync to get response back but request always runs sync. not like fired and then when response arrives read the data.
It works fine but I want it to be async and not waiting on each request. How to achieve that or please explain await async in this context? Trying to read blogs and articles but I don't get how it will work.
The syntax you're probably looking for is this:
public async Task<JArray> GetContentAsync(... updateObject)
{
HttpResponse response = await client.PostAsJsonAsync("webapimethod", updateObject);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<JArray>();
}
}
Here your thread from the GetContentAsync() method will be put back on the threadpool while the client.PostAsJsonAsync is happening due to the await keyword.
Then you can create all the tasks in your method which calls it:
var updateData = fetchData();
var tasks = updateData.Select(d => GetContentAsync(d));
var result = (await Task.WhenAll(tasks)).ToList();
The Select will create a task for each of your result.
The await Task.WhenAll will unwrap the Task<JArray> and create a List<JArray>
You have to move foreach out of RunAsync method.
To get RunAsync in a foreach loop to work in async mode, you have to create several tasks and then call Task.WaitAll

.NET HttpClient hangs when using await keyword?

After considering this interesting answer HttpClient.GetAsync(...) never returns..., I still have a situation where my HttpClient is not returning when I use await (sample code below). In addition. I use this helper routine from both asp.net MVC5 Controllers (UI-driven) and the WebApi. What can I do to:
Use await instead of the (dreaded) .Result and still have this function return?
Reuse this same routine from both MVC Controllers and WebApi?
Apparently, I should replace the .Result with .ConfigureAwait(false) but this seems to contradict with the fact that "Task4" in the post cited above works fine with an await httpClient.GetAsync. Or do I need separate routines for Controller and WebApi cases?
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// Why does this never return when await is used?
HttpResponseMessage response = hclient.GetAsync("api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
UPDATE: My call chain, which starts with a Telerik Kendo Mvc.Grid DataBinding call is as follows:
[HttpPost]
public async Task<ActionResult> TopLicenseGrid_Read([DataSourceRequest]DataSourceRequest request)
{
var res = await GetLicenseInfo();
return Json(res.ToDataSourceResult(request)); // Kendo.Mvc.Extensions.DataSourceRequest
}
Then:
private async Task<IEnumerable<CsoPortalLicenseInfoModel>> GetLicenseInfo()
{
...
// Never returns
var qry = #"app_metadata.tc_app_user.country:""DE""";
return await TcSearchMultiUsersAsync(qry);
}
Then, the routine shown in full above but now WITHOUT the .Result:
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// This now returns fine
HttpResponseMessage response = hclient.GetAsync("api/v2/users?search_engine=v2&q=" + elasticQuery");
if (response.IsSuccessStatusCode)
{
// This returns my results fine too
var content = await response.Content.ReadAsStringAsync();
// The following line never returns results. When debugging, everything flows perfectly until I reach the following line, which never
// returns and the debugger returns me immediately to the top level HttpPost with a result of null.
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
You need to await everything. It's not enough that you await on one of them, you need to await on all of them:
This:
HttpResponseMessage response = hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
Should be:
HttpResponseMessage response = await hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2");
It is enough to have one blocking call to .Result in order to deadlock. You need "async all the way".

Implementing a synchronous method with calling the <asyncmethod>.Result

I have this async method:
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
var readAsStringAsync = await message.Content.ReadAsStringAsync();
return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings);
}
}
Where FromJsonAsync is implemented as an extension method:
public async static Task<T> FromJsonAsync<T>(this string data, JsonSerializerSettings settings) where T : new()
{
return (T)(await JsonConvert.DeserializeObjectAsync<T>(data, settings));
}
Now I want to add a regular synchronous Post method and I thought the implementation would be:
public RES Post<RES>(string url, string content) where RES : new()
{
return PostAsync<RES>(url, content).Result;
}
But this doesn't really work. I see that the request is sent via a Http sniffer and I get a response back, but I get stuck when debugging and can't continue.
BTW, this does work (with Result instead of await):
public RES Post<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json")).Result;
var readAsStringAsync = message.Content.ReadAsStringAsync().Result;
return readAsStringAsync.FromJson<RES>(mySerializerSettings);
}
}
Where FromJson is implemented as an extension method:
public static T FromJson<T>(this string data, JsonSerializerSettings settings) where T : new()
{
return (T)JsonConvert.DeserializeObject<T>(data, settings);
}
The application is a web backend (WebApi).
What am I doing wrong?
You probably have a deadlock on your hands.
Asp.net uses a SynchronizationContext to post continuations back to the request context. If the context is blocked (like it is in your case on PostAsync<RES>(url, content).Result) then the continuation can't be executed and so the async method can't complete and you have a deadlock.
You can avoid it by using ConfigureAwait(false):
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
var readAsStringAsync = await message.Content.ReadAsStringAsync().ConfigureAwait(false);
return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings).ConfigureAwait(false);
}
}
But it's better to just avoid blocking synchronously on async code to begin with and having two different versions for sync and async.
Although possible, I wouldn't use the answer provided by #i3arnon. Generally, you shouldn't block on async code. Although ConfigureAwait(false) does work, it can lead to confusion in your code-base where other developers may also end up blocking using .Result, without using ConfigureAwait or understanding the implications of that.
Instead, expose synchronous methods which are really synchronous:
public RES Post<RES>(string url, string content) where RES : new()
{
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
var result = client.UploadString(url, content);
return JsonConvert.DeserializeObject<RES>(result, jsonSerializerSettings);
}
}
It seems you have a non-async function and you want to start a task that will call PostAsync and wait for this task to finish and return the result of the Task. Is this your problem?
To start a Task, use Task.Run( () => ...);
To wait for the Task use Task.Wait(...);
To see if the task stopped because of an exception: Task.IsFaulted
The result of the task is in Task.Result
Your code could be:
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
// start the task that will call PostAsync:
var postTask = Task.Run( () => PostAsync(url, content));
// while this task is running you can do other things
// once you need the result: wait for the task to finish:
postTask.Wait();
// If needed check Task.IsFaulted / Task.IsCanceled etc. to check for errors
// the returned value is in Task.Result:
return postTask.Result;
}

Categories

Resources