C# HttpClient to post information without waiting for response - c#

I am using HttpClient class in my asp.net web api 2 application to post some information to a endpoint. I just want to post the information without waiting for a response. Is this the right syntax
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
var gizmo = new Product() { Name = "Gizmo", Price = 100, Category = "Widget" };
var response = await client.PostAsJsonAsync("api/products", gizmo);
}

I just want to post the information without waiting for a response
Not awaiting an async method in WebAPI will result in a runtime exception, as the AspNetSynchronizationContext is aware of any triggered asynchronous operations. If it notices a controller action completes before the async operation has, it will trigger the said exception. More on that in ASP.NET Controller: An asynchronous module or handler completed while an asynchronous operation was still pending
If you want to use a fire and forget semantics, you need to queue the delegate via HostingEnvironment.QueueBackgroundWorkItem if you're using .NET 4.5.2 and above. If not, you can defer to using BackgroundTaskManager
Keep in mind this kind of design isn't really suitable for WebAPI. It doesn't scale if you're triggering this action call frequently. If this style happens often, consider using something more suitable such as a message broker.

To implement the async Task in ASP.NET refer to the following sample syntax:
protected void Page_Load(object sender, EventArgs e)
{
try
{
RegisterAsyncTask(new PageAsyncTask(LoadUrlContent));
}
catch {}
}
protected async Task LoadUrlContent()
{
try
{
// Add your code here, for example read the content using HttpClient:
string _content = await ReadTextAsync(YourUrl, 10);
}
catch { throw; }
}
Also, set <%# Page ... Async="true" %> at page level.
Following sample code snippet shows the use of HttpClient (call this sample function from LoadUrlContent():
protected async Task<string> ReadTextAsync(string Url, int TimeOutSec)
{
try
{
using (HttpClient _client = new HttpClient() { Timeout = TimeSpan.FromSeconds(TimeOutSec) })
{
_client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/html"));
using (HttpResponseMessage _responseMsg = await _client.GetAsync(Url))
{
using (HttpContent content = _responseMsg.Content)
{
return await content.ReadAsStringAsync();
}
}
}
}
catch { throw; }
}
You can modify this code base pertinent to your particular task.
Hope this may help.

Related

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.

Is it possible to stop using async and await where required

I'm implementing code from a developer that are using async functions.
Example:
public async Task<string> GetDataAsync(string jsonString = "")
{
string url = $"{this.url}";
using (HttpClient httpClient = new HttpClient())
{
using (StringContent body = new StringContent(jsonString, Encoding.UTF8, "application/json"))
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.accessToken);
using (HttpResponseMessage response = await httpClient.PostAsync(url, body))
{
switch (response.IsSuccessStatusCode)
{
case true:
return await response.Content.ReadAsStringAsync();
default:
throw new Exception($"Error calling url: {url}. HTTP status: {response.StatusCode}");
}
}
}
}
}
And I don't need, nor do I want, to call anything asynchronously. But the problem is that async functions is bubbling all the way up through my functions and so I can't just stop using it, and since HttpClient doesn't have a Post() function to use instead of PostAnync() then I feel stuck in this asynchronous cage.
Is there a trick or whatever to call an async function normally, stopping threading from bubbling up through all parent functions?
Or is the only solution to find a package without async functions?
The short answer is - no, there's no generic way to make a synchronous function out of a a task-based asynchronous one.
The problem is that you don't know how it is implemented internally. Say, the asynchronous function is implemented using async and it's running (partially) in the context of the primary thread. Then, if the caller code is trying to block the primary thread by a blocking call, then the async function is blocked, too, which is causing a deadlock.
But, in your particular case you can try to create a new task, call the async function from that task and take its result. There are good chances that it will work, but no guarantee (as mentioned above).
The code would look like this:
using (var response = Task.Run(() => httpClient.PostAsync(url, body).Result).Result)
{
...
}

Difference between HttpClient PostAsync and SendAsync

Working on a project where a WPF front end, and trying to get a handle on async calls to HttpClient and I've been going around and around trying to get PostAsync to work, but it routinely appears to deadlock, or at least the post response times out, even with large values for timeout, and a visible response in fiddler.
So, after a while I decided to try out a few other methods on HttpClient, and they worked, first try. No idea why.
I'm clean all the way to my WPF button with awaits, asyncs, and .ConfigureAwait(false) (I think):
Button:
private async void Generate_Suite_BTN_Click(object sender, RoutedEventArgs e)
{
await suiteBuilder.SendStarWs().ConfigureAwait(false);
}
XmlDoc Load:
internal async Task SendStarWs()
{
var xmlDoc = new XmlDocument();
xmlDoc.Load("C:\\Temp\\file.xml");
await StarWSClient.SendStarMessage(xmlDoc).ConfigureAwait(false);
}
SendMessage:
private static readonly HttpClient Client = new HttpClient {MaxResponseContentBufferSize = 1000000};
public static async Task<STARResult> SendMessage(vars)
{
var response = await SendRequestAsync(url, contentNew, Client).ConfigureAwait(false);
return new STARResult(response, hash);
}
This call '500s' against my endpoint immediately, which I'd expect:
var response = await SendRequestAsync(url, contentNew, Client).ConfigureAwait(false);
private static async Task<HttpResponseMessage> SendRequestAsync(string adaptiveUri, StringContent content, HttpClient httpClient)
{
HttpResponseMessage responseMessage = null;
try
{
responseMessage = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, adaptiveUri)).ConfigureAwait(false);
}
catch (Exception ex)
{
if (responseMessage == null)
responseMessage = new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError,
ReasonPhrase = $"SendRequestAsync failed: {ex.Message}"
};
}
return responseMessage;
}
The Post variant returns a TaskCancellationException, with timeout message regardless of timeout value:
var response = await PostRequestAsync(url, contentNew, Client).ConfigureAwait(false);
private static async Task<HttpResponseMessage> PostRequestAsync(string adaptiveUri, StringContent content, HttpClient httpClient)
{
HttpResponseMessage responseMessage = null;
try
{
responseMessage = await httpClient.PostAsync(adaptiveUri, content).ConfigureAwait(false);
}
catch (Exception ex)
{
if (responseMessage == null)
responseMessage = new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError,
ReasonPhrase = $"PostRequestAsync failed: {ex.Message}"
};
}
return responseMessage;
}
My endpoint responds normally to our other software, so I'm pretty sure the endpoint is solid, I can't fathom why the post response is blocked, when the send isn't.
SendAsync can make any http verb request depending on how you set that property. PostAsync and similar are just convenience methods. Those convenience methods use SendAsync internally which is why when you derive a handler you only need to override SendAsync and not all of the send methods.
To your other question though:
When you use SendAsync you need to create the content and pass it. Your only sending an empty message. The 500 likely means that the api got null from the model binding and kicked you back. Just as #John commented.

Exception while posting to web API, HTTPClient already disposed

I'm posting a bytearray from an Android App in Xamarin.Forms to an .NET Core 2.0 WebAPI. However, I'm getting an exception saying that the NetworkStream already is disposed;
Code making the request;
public async Task PostImageAsync(ImageDTO image)
{
var content = new MultipartFormDataContent();
var byteArrayContent = new ByteArrayContent(image.Content);
content.Add(byteArrayContent, image.FileTile, image.FileName);
try
{
using (var httpClient = GetNewHttpClient())
{
SetBearerToken(httpClient);
var response = await httpClient.PostAsync($"{_apiUrl}/api/images/upload", content);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
}
catch (Exception e)
{
//Exception occurs here
var msg = e.GetBaseException().Message;
throw;
}
}
Code to get the HttpClient
private HttpClient GetNewHttpClient()
{
//HttpClientHandler is a global variable
var httpClient = new HttpClient(HttpClientHandler, false) {BaseAddress = new Uri(_apiUrl)};
return httpClient;
}
API Endpoint
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null || file.Length == 0) return BadRequest();
return Ok();
}
EDIT - SetBearerToken Method
private static void SetBearerToken(HttpClient client)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", App.StoredToken);
}
The Exception:
cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream'.
It feels like a really obvious mistake I'm making here, but I can't get my head around it. Anybody has any ideas?
Don't dispose objects inside async functions
A using statement in an async method is "odd" in that the Dispose
call may execute in a different thread to the one which acquired the
resource (depending on synchronization context etc) but it will still
happen... assuming the thing you're waiting for ever shows up or
fail, of course. (Just like you won't end up calling Dispose in
non-async code if your using statement contains a call to a method
which never returns.)
#jon-skeet https://stackoverflow.com/a/16566605/2228916
Don’t dispose of the HttpClient:
https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Also noticed that you set _apiUrl as the BaseAddress and prefix the url in the post. Pick one or the other.

Unresponsiveness with async event handlers in WPF in .NET 4.5

I have created a simple async operation which is being kicked of when the button is clicked. Here is the whole code:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private async void Button_Click_1(object sender, RoutedEventArgs e) {
var htmlString = await DowloadPage("http://example.com");
txtBlock1.Text = htmlString;
}
public async Task<string> DowloadPage(string uri) {
using (WebClient client = new WebClient()) {
var htmlString = await client.DownloadStringTaskAsync(uri);
return htmlString;
}
}
}
Very easy. But when I click the button, I experience unresponsiveness on the UI thread. When I try to move around the window while the page is being downloaded, I am unable to.
Any idea what is going wrong?
Edit:
I tried with HttpClient in .NET 4.5 and it worked out pretty great as expected:
public async Task<string> DowloadPage(string uri) {
using (HttpClient client = new HttpClient()) {
var response = await client.GetAsync(uri);
var htmlString = await response.Content.ReadAsStringAsync();
return htmlString;
}
}
WebClient uses HttpWebRequest, which unfortunately is not very asynchronous, even if you use the "asynchronous" methods. It does a blocking DNS lookup, at least. It may also block during proxy negotiation and/or the initial HTTP connection.
An older release of HttpClient was just using a wrapper around HttpWebRequest. I requested a truly-asynchronous HttpClient, but never heard a response. The last time I checked HttpClient, it was still part of MVC; ASP.NET Web API wasn't around at that time, so they may have fixed HttpClient since then. Or the difference in behavior between WebClient and HttpClient on your machine may just have to do with DNS caches or some such.

Categories