Didn't get response from GetAsync API of HttpClient in MVC applications - c#

I am facing an issue regarding not getting response from GetAsync API of HttpClient in MVC Applications(Target Framework - .Net Framework 4.7) whereas getting response in web services and console applications with same snippet. Here I have attached code snippet which I am trying to execute.
public void Get()
{
var response = Gettasks().Result;
}
public static async Task<HttpResponseMessage> GetTasks()
{
var response = new HttpResponseMessage();
try
{
using (var client = new HttpClient())
{
response = await client.GetAsync("https://www.google.com");
}
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
return response;
}
I am getting stuck on response = await client.GetAsync("https://www.google.com"); this line and not getting any response after executing this statement.
If anyone can please suggest solution for this or provide fix/solution which works for you.

You're seeing a deadlock because the code is blocking on an asynchronous method.
The best fix is to remove the blocking:
public async Task Get()
{
var response = await Gettasks();
}
This deadlock happens because await captures a context, and ASP.NET (pre-Core) has a context that only allows one thread at a time, and the code blocks a thread (.Result) in that context, which prevents GetTasks from completing.
Both the context and the blocking are necessary to see this kind of deadlock. In the other scenarios, there is no context, so that is why the deadlock does not occur. Since ASP.NET (pre-Core) has a context, the proper solution here is to remove the blocking.

Not sure whether you have tried following which is working for me.
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("BaseAddress"));
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var requestUri = Environment.GetEnvironmentVariable("Uri");
HttpResponseMessage response = await client.GetAsync(requestUri);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
}
}

Related

HttpClient - PostAsync doesn’t return

Does anyone know why HttpClient - PostAsync doesn’t return. It just does nothing. I have had it working occasionally especially for one off posts but it seems sometimes to not do anything especially under load and it doesn't throw an exception which of course makes my code unreliable and hard to debug.
I have tried adding ConfigureAwait(false) It makes not difference.
I suspect the Task is failing to 'pack'
This is in a core 3.0 console app run on macOS Catalina using visual studio code
This code is pretty much copied from Microsoft's documentation and I’m calling Microsoft Graph when posting.
public static async Task PostAsync(HttpClient httpClient, string url, string token, HttpContent content, Action<JObject> processResult, ILogger log)
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// content.Headers.Clear();
content.Headers.Add("Content-Type", "application/json");
try
{
HttpResponseMessage response = await httpClient.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(json) as JObject;
processResult(result);
}
else
{
var errorContent = await response.Content.ReadAsStringAsync();
log.LogError(errorContent);
}
}
catch (System.Exception ex)
{
log.LogError(ex, ex.Message);
throw;
}
}
Here is an example of the calling code
public async Task SendInvitation(string token, Invitation invitation, ILogger logger)
{
var stringContent = new StringContent(JsonConvert.SerializeObject(invitation), Encoding.UTF8, "application/json");
await HttpHelpers.PostAsync(
Client,
"https://graph.microsoft.com/v1.0/invitations",
token,
stringContent,
result => logger.LogInformation(DebugHelpers.Print(result)),
logger);
}
Answered (Sort of)
If I change
HttpResponseMessage response = await httpClient.PostAsync(url, content);
to
HttpResponseMessage response = httpClient.PostAsync(url, content).GetAwaiter().GetResult();
It seems to work but it's slow because what I'm doing is using blocking code. I think this is a quirk of core 3 on macOS. I don't like that this is happening.
More Info
I'm doing a lot of looping.
It seems that if I put all the things I'm awaiting in a taskList it behaves properly.
\\ Pseudo Code
var taskList = new List<Task>();
foreach(var thing in things){
taskList.Add(HttpHelpers.PostAsync(...things));
}
await Task.WhenAll(taskList);
Please check whether all calls you make in the code execution path support asynchronousity. For example once I spent quite some time figuring out a nuget package called CommandLineParser did not support async calls. I was using it like so :
public static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed(async options =>
{ await httphelper.PostAsync(...);
}
}
I fixed the issue by changing it to something like
public static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed(options =>
{ httphelper.PostAsync(...).Result;
}
}
So please check you are not using some calls that do not support async in the way.
You can wait for HttpResponseMessage certain condition. Inspect HttpResponseMessage in debug, with 2 given steps. Screenshot of debug process: postAsync debug
return HttpResponseMessage from method with postAsync:
private static async Task<HttpResponseMessage> methodWithPostAsync(){
...
response = await client.PostAsync(url, data);
return response
}
call method and wait for response message status:
Task<HttpResponseMessage> content= methodWithPostAsync();
while (!content.IsCompleted)
{
Console.WriteLine("busy");
System.Threading.Thread.Sleep(1000);
}
try ConfigureAwait like this
HttpResponseMessage response = await httpClient.PostAsync(url, content).ConfigureAwait(false);

Deadlock on .Result from Web UI

I was reading the following topic http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
and decided to write a common utility method in my library to do a GET on remote url via HTTPClient
public static async Task<T> GetAsync<T>(HttpGetObject getObject)
{
string baseUrl = getObject.BaseUrl;
string actionUrl = getObject.ActionRelativeUrl;
string acceptType = getObject.AcceptType;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(acceptType));
AddCustomHeadersToHttpClient(client, getObject);
// HTTP GET
HttpResponseMessage httpResponseMessage = await client.GetAsync(actionUrl).ConfigureAwait(false);
if (httpResponseMessage.IsSuccessStatusCode)
{
T response = await httpResponseMessage.Content.ReadAsAsync<T>().ConfigureAwait(false);
return response;
}
else
{
string message = httpResponseMessage.Content.ReadAsStringAsync().Result;
throw new Exception(message);
}
}
return default(T);
}
I know the "await httpResponseMessage.Content.ReadAsAsync().ConfigureAwait(false)" will prevent the deadlock in the above code
First:
My query is for "string message = httpResponseMessage.Content.ReadAsStringAsync().Result" line, will .Result can cause deadlock or not in that line?
Second:
If I call that code from UI like this:
public static object DoGet()
{
// Build getObject
var task = Utility.GetAsync(getObject);
task.Wait();
var response = task.Result;
return response;
}
Will that cause a deadlock?
Please note that I know to avoid all the mess with async-await, all the methods from UI to DAL must be async-await but I am not in position at this moment to change all that structure, my goal at this moment is to call HttpClient library and do a few GET operations.
So my questions is that will the above code can cause a deadlock?
Third:
Is task.Wait(); even needed in the above code?
In the general case, you should assume that yes, calling .Result or .Wait() on anything awaitable is dangerous and can deadlock (unless you are the library issuing the task, and you understand the full context). It is possible that it will work OK in some specific cases, but you should not rely on that behaviour, even if it works today.

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

HttpClient in using statement causes Task cancelled

I created a FileResult : IHttpActionResult webapi return type for my api calls. The FileResult downloads a file from another url and then returns the stream to the client.
Initially my code had a using statement like below:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
using (var httpClient = new HttpClient())
{
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
}
return response;
}
catch (WebException exception)
{...}
}
However this would intermittently cause a TaskCanceledException. I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.
Why does that task get canceled? It is not because of a timeout since this has happened on the smallest requests and doesn't always occur on large requests.
When I removed the using statement the code worked properly:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
var httpClient = new HttpClient();
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
return response;
}
catch (WebException exception)
{...}
}
Any idea why the using caused the issue?
I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.
But what does that task do? It gets the stream. So, your code ends up with a Stream that may or may not be completely read when it closes the HttpClient.
HttpClient is specifically designed for reuse (and simultaneous use), so I recommend removing the using completely and moving the HttpClient declaration to a static class member. But if you want to close and reopen the clients, you should be able to get it working by reading the stream entirely into memory before closing the HttpClient.
I had a similar issue with Task Canceled exceptions. If you try catching AggregateException or having a catch all Exception block underneath your WebException, you may well find that you catch it, with one exception with the entry stating "A task was canceled"
I did some investigation and found that the AggregateException is quite misleading as described in various threads;
Setting HttpClient to a too short timeout crashes process
How can I tell when HttpClient has timed out?
Bug in httpclientgetasync should throw webexception not taskcanceledexception
I ended up changing my code to set an explicit timeout (where asyncTimeoutInMins is read from the app.config file);
string jsonResponse = string.Empty;
try
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(Properties.Settings.Default.MyWebService);
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.Timeout = new TimeSpan(0, asyncTimeoutInMins, 0);
HttpResponseMessage response;
response = await httpClient.GetAsync("/myservice/resource");
// Check the response StatusCode
if (response.IsSuccessStatusCode)
{
// Read the content of the response into a string
jsonResponse = await response.Content.ReadAsStringAsync();
}
else if (response.StatusCode == HttpStatusCode.Forbidden)
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.Unauthorised);
}
else
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.ApplicationError);
}
}
}
catch (HttpRequestException reqEx)
{
Logger.Instance.Error(reqEx);
Console.WriteLine("HttpRequestException : {0}", reqEx.InnerException.Message);
Environment.Exit((int)ExitCodes.ApplicationError);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
throw;
}
return jsonResponse;

C# Web API call

This is the first time i've tried making a call to an API and I'm struggling a little. I keep getting back my error message, I plan on using the json response to populate my object. The OMDB api instructions are here (not helpful though): http://www.omdbapi.com/
private static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://www.omdbapi.com/?");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("t=Captain+Phillips&r=json").Result;
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Error with feed");
}
}
}
You have placed the question mark (?) on the wrong place. Try like this:
private static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://www.omdbapi.com");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("?t=Captain+Phillips&r=json");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Error with feed");
}
}
}
Notice that the question mark is here:
HttpResponseMessage response = await client.GetAsync("?t=Captain+Phillips&r=json");
and not on the base url as you placed it.
Also in order to properly write your asynchronous method you need to await on it inside and not be eagerly calling the .Result property which of course is a blocking operation.

Categories

Resources