I'm completely new to using async calls and await. I have the below unit test function:
public async static void POSTDataHttpContent(string jsonString, string webAddress)
{
HttpClient client = new HttpClient();
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
Console.WriteLine("response is: " + response);
}
The test completes without error, but I never see the Console.WriteLine print statement show up in output - I'm not sure why. I've been looking around and it sounds like I may need to set this up as a task? Could someone point me in the proper direction?
Since you are already awaiting an HttpResponseMessage, a simple (and consistent) solution is to return Task<HttpResponseMessage>.
var x = await POSTDataHttpContent("test", "http://api/");
public async Task<HttpResponseMessage> POSTDataHttpContent(
string jsonString, string webAddress)
{
using (HttpClient client = new HttpClient())
{
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
Console.WriteLine("response is: " + response);
return response;
}
}
That said, you also need to ensure that your test setup is correct. You cannot properly call an async method from a synchronous test. Instead, mark your test async as well and await the method you are calling. Furthermore, your test method must be marked as async Task as well since neither MS Test Runner nor other tools (NCrunch, NUnit) will properly deal with an async void test method:
[TestMethod]
public async Task TestAsyncHttpCall()
{
var x = await POSTDataHttpContent("test", "http://api/");
Assert.IsTrue(x.IsSuccessStatusCode);
}
I think the best thing to do here for you would be to opt for a Task return type instead of a void.
public async Task POSTDataHttpContent(string jsonString, string webAddress)
{
using (HttpClient client = new HttpClient())
{
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
// Assert your response may be?
}
}
And if you are really adamant about not using Tasks (which is not a good idea):
public void POSTDataHttpContent(string jsonString, string webAddress)
{
var Task = Task<HttpResponseMessage>.Run(async () => {
using (HttpClient client = new HttpClient())
{
StringContent stringContent = new StringContent(jsonString);
HttpResponseMessage response = await client.PostAsync(
webAddress,
stringContent);
return response;
}
});
Task.Wait();
Assert.IsNotNull(Task.Result);
}
Related
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);
I am trying to send an Http Get message to the Google location Api which is supposed to have Json data such as this
https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt
as you noticed the response is in Json. I want to give a a Http Call to that URL and save the Json content in a variable or string. My code does not give out any errors but it is also not returning anything
public async System.Threading.Tasks.Task<ActionResult> GetRequest()
{
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
string data = response.Content.ToString();
return data;
}
I want to send out a Get Request using HttpClient() or anything that will send out the URL request and then save that content into a string variable . Any suggestions would be appreciated, again my code gives no errors but it is not returning anything.
Use ReadAsStringAsync to get the json response...
static void Main(string[] args)
{
HttpClient client = new HttpClient();
Task.Run(async () =>
{
HttpResponseMessage response = await client.GetAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
string responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
});
Console.ReadLine();
}
If you use response.Content.ToString() it is actually converting the datatype of the Content to string so you will get System.Net.Http.StreamContent
Try this.
var client = new HttpClient();
HttpResponseMessage httpResponse = await client.GetAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
string data = await httpResponse.Content.ReadAsStringAsync();
How about using the Json framework for .NET powered by Newtonsoft ?
You can try to parse your content to a string with this.
Well, your code can be simplified:
public async Task<ActionResult> GetRequest()
{
var client = new HttpClient();
return await client.GetStringAsync("https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt");
}
However...
My code does not give out any errors but it is also not returning anything
This is almost certainly due to using Result or Wait further up the call stack. Blocking on asynchronous code like that causes a deadlock that I explain in full on my blog. The short version is that there is an ASP.NET request context that only permits one thread at a time; await by default will capture the current context and resume in that context; and since Wait/Result is blocking a thread in the request context, the await cannot resume executing.
Try this
public async static Task<string> Something()
{
var http = new HttpClient();
var url = "https://maps.googleapis.com/maps/api/geocode/json?address=Los%20Angeles,CA=AIzaSyDABt";
var response = await http.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<string>(result);
return result;
}
return "";
}
var result = Task.Run(() => Something()).Result;
The following code just hangs when called from an ASP.NET app:
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = await client.GetByteArrayAsync(sUrl);
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
If I change to following it works fine:
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = null;
var task = Task.Run(async () => { urlContents = await client.GetByteArrayAsync(strUrl); });
task.Wait();
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
Is the fact that method signature return is a Task<XPathNavigator> causing the issue? Thank you.
Somewhere higher up the call stack there is a .Wait() being performed on the task that is returned from UspsCreateAndPostRequest.
Because you wrapped the call inside a Task.Run you lost the execution context, that is why it works. Doing
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = await client.GetByteArrayAsync(sUrl).ConfigureAwait(false);
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
would achieve the same goal with less resources (but it would be even better to fix the wait higher up on the chain).
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;
}
I have a Portable Class Library (PCL) method like this:
public async Task<string> GetLineStatuses()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
{
return response.GetResponseStream().ReadAllText();
}
}
My ASP.NET Web Api method looks like this:
public async Task<HttpResponseMessage> Get()
{
HttpResponseMessage response = new HttpResponseMessage();
string statuses = await service.GetStatuses();
response.Content = new StringContent(statuses);
return response;
}
What are the implications of returning a Task in Web API. Is this allowed? The only reason I want to use await is so I can use a Portable Class Library (PCL). What is the best practice? Should I have a syncronous version of my method and an asyncronous version? What are the performance and code readability and maintainability implications?
Also would I have the same effect if I returned Task<string> rather than Task<HttpResponseMessage>?
Async and await are perfectly acceptable in ASP.NET. Here's a Scott Handselman video demoing it: http://www.asp.net/vnext/overview/aspnet/async-and-await
"Also would I have the same effect if I returned Task<string> rather than Task<HttpResponseMessage>?"
Not really sure what you mean by this. The Task is like a container for the object, so a Task<string> would contain your string result and a Task<HttpResponseMessage> would contain your HttpResponseMessage result... Is that what you mean? I think either method is perfectly acceptable. If you just need the string, then go with that. No point in returning more than you need.
as alternative:
public static async Task<string> CallGET(string requestUri, string id = "")
{
string responseData;
using (var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true }))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Uri.TryCreate(new Uri(baseURI), $"{requestUri}{(string.IsNullOrEmpty(id) ? string.Empty : $"/{id}")}", out Uri fullRequestUri);
using (var response = await client.GetAsync(fullRequestUri))
{
responseData = await response.Content.ReadAsStringAsync();
}
return responseData;
}
}
and call would be:
var getListUsersResult = Utils.CallGET($"/v1/users").Result;
var resultset= JsonConvert.DeserializeObject(getListUsersResult, typeof(List<UsersDTO>)) as List<UsersDTO>;
UserDTO r = users.Where(d => d.Name.ToLower().Contains("test")).FirstOrDefault();
and another call for one item:
var getUser = Utils.CallGET($"/v1/users", $"{USER_ID}").Result;
var getUserResponse = JsonConvert.DeserializeObject(getUser, typeof(UserDTO)) as UserDTO;