Best practice for making a HTTP Post from asp.net Page_Load? - c#

Seems like simple task, but the interwebs are flooded with different ways to approach this, so what is best practice for making a simple HTTP POST from asp.net Page_Load (I'm on .net 4.7) ?
Many resources point to the fact that HttpClient.PostAsync is the most lightweight approach.
However, the example here: https://learn.microsoft.com/en-us/aspnet/web-forms/overview/performance-and-caching/using-asynchronous-methods-in-aspnet-45 - uses WebClient.
Also, both of theses approaches require setting Page Async="true" on the page - I am seeing conflicting information on whether this is actually the right approach.
Context: My page looks like it is spending a lot of time in BLOCKED_TIME during two requests that are made in Page_Load.
I suspect this is due to the way I currently make the POST:
public string makePost(string parametersToPassOn, string command)
{
HttpContent c = new StringContent(parametersToPassOn, Encoding.UTF8, "application/json");
var t = Task.Run(() => fireRESTcall(new Uri(walletProxyURL + command), c));
t.Wait();
return t.Result;
}
static async Task<String> fireRESTcall(Uri u, HttpContent c)
{
var response = string.Empty;
using (var client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = u,
Content = c
};
HttpResponseMessage result = await client.SendAsync(request);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
}
return response;
}
Any help/thoughts would be greatly appreciated.

During the time your page makes an outgoing HTTP request, it can't do anything useful other than waiting for the response. See also Understanding BLOCKED_TIME in PerfView
You probably can't do anything about this delay, as you need the external API for some data, and you need to wait for it to complete in order to do something useful with that data.
So it's advisable to use async/await all the way, using RegisterAsyncTask():
void Page_Load()
{
RegisterAsyncTask(new PageAsyncTask(CallApiAsync));
Page.ExecuteRegisteredAsyncTasks();
}
async Task CallApiAsync() // calls
async Task<string> MakePost() // calls
async Task<String> FireRESTcall()
This frees up the thread to handle a new incoming request, until the outgoing request is finished and the incoming request continues on the same or another thread pool thread.
For this to work with WebForms, you do need Async="true" on your page.

Related

Run a async function without waiting for the response in c# webforms (fire and forget)

I have a button that makes a REST request to create a document:
private void Click(object sender, ActionBaseEventArgs e)
{
RequestQuotationDocument(quotation.Oid).ConfigureAwait(false);
}
private async System.Threading.Tasks.Task RequestQuotationDocument(int quotationId)
{
HttpClient client = new HttpClient();
var values = new
{
DocumentType = "Quotation",
Id = quotationId.ToString()
};
var content = new StringContent(JsonConvert.SerializeObject(values), System.Text.Encoding.UTF8,
"application/json");
await client.PostAsync(url, content);
}
However the page is still waiting for the response of the POST request. How do I make this truly async?
How do I make this truly async?
In ASP.NET, await yields to the thread pool, not the client.
If possible, I recommend changing the client so that a longer request is handled appropriately; e.g., calling this via JavaScript instead of a click handler, and updating the page when it completes.
If you really need to return early from an web request, then you should implement a basic distributed architecture. As described on my blog, this consists of two parts:
A durable queue of work.
A backend processor of that queue.

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.

HttpClient.PostAsJsonAsync never sees when the post is succeeding and responding

We are using an HttpClient to post json to a restful web service. In one instance, we are running into something that has us baffled. Using tools like postman, fiddler etc, we can post to an endpoint and see that it is working. When we do the same with HttpClient.PostAsJsonAsync, we can verify in the software we are posting to that it received the data just fine. However, our PostAsJsonAsync will always eventually time out rather than give us a response.
We have worked with the team that created the service we are consuming, plus our additional testing on our side, and we have not yet been able to truly time out that service.
Every time we do a post with HttpClient, we then can verify that the target software we post to does indeed get the data. Any time we do a post to that target software from any other tool, we always very quickly see a response with status code of 200. Something about HttpClient is failing to accept the response from this particular service. Does anyone have an idea what we can look at from here?
Here's the code (though it is so cookie cutter that I hardly feel it is needed)
public string PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = client.PostAsJsonAsync(useUrl, o).Result;
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return response.Content.ReadAsStringAsync().Result;
}
return "";
}
}
This:
var response = client.PostAsJsonAsync(useUrl, o).Result;
Is causing you code to deadlock. This is often the case when blocking on async API's, and that's why you're experiencing the "I don't see any response coming back" effect.
How is this causing a deadlock? The fact that you are executing this request in an environment that contains a synchronization context, perhaps one which belongs to the UI. It's executing the async request, and when the response arrives, it continues via an IO completion thread which attempts to post the continuation onto that same UI context, which is currently blocked by your .Result call.
If you want to make an HTTP request synchronously, use WebClient instead. If you want to take advantage of the async API properly, then await instead of block with .Result.
I had the same issue and this SO answer fixed it for me.
In a nutshell, you have to use the ConfigureAwait(false) extension to avoid the deadlock:
var response = await client.PostAsJsonAsync(useUrl, o).ConfigureAwait(false);
Is there a reason why you're not following the async await pattern? You're calling an async method, but not awaiting it. You didn't say if the code calling your REST service was a Windows Forms or ASP.NET application, but that .Result is probably causing you issues.
Can you restructure your method like this:
public async Task<string> PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
This is a slight modification to #Justin Helgerson's solution. There are 2 blocking .Result calls in your method; once you go async you should fix them both.
public async Task<string> PostDataAsync(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
Note I've also renamed the method to PostDataAsync in accordance with the TAP pattern.
System.Net.ServicePointManager.Expect100Continue = false;
That one line of code in our case fixed the problem. A developer from a different team offered that suggestion, and it works. I have yet to google it and read up on it enough to offer an explanation of what that is addressing.

Application enter in a "wait status" while HttpRequest operation

I'm doing an HttpRequest to an Url that returns me an xml content. So I try to make an asynchronous request but during this request the application is stopped (pause) and it seems that it cannot get a response (the url inside a browser works perfectly and it returns what I expected).
Here is my code:
public static async Task<String> getResponse(String url)
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage request = await httpClient.GetAsync(url);
String stream = await request.Content.ReadAsStringAsync();
return stream;
}
and I take this with:
String response = UtilityClass.getResponse(requestUrl).Result;
Anyone can help me please?
What happends here is a deadlock. Without too much information i see you're blocking on an asynchronous operation when calling
string response = UtilityClass.getResponse(requestUrl).Result
What happends is when you await inside getResponse, the TaskAwaitable being generated captures your SynchronizationContext in order to marshal the continuation back on to the same context in which it was called. But, when it trys to marshal work bacl to the captured context it cant, because Result is blocking the thread. This is why you should never block on async code. This can be fixed by using ConfigureAwait(false) which tells the TaskAwaitable not to marshal the continuation back and simply execute on the current thread that invoked it:
public static async Task<String> getResponse(String url)
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage request = await httpClient.GetAsync(url).ConfigureAwait(false);
String stream = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
return stream;
}
Another solution, which is better IMO, is to use the async method properly and await on it, instead of block:
string response = await UtilityClass.getResponse(requestUrl)
That would require you to add the async keyword to the calling method. If you cant, maybe a synchronous http request might be the better solution.
Edit
To quote #EricLippert comment which makes this easier to understand:
The key thing to understand here is that result = task.Result; means do nothing else until the result is available, and result = await task; means do something else until the result is available. Both turn a Task<string> into a string, but one blocks and the other does not
There are many good posts explaining the fundamental s of async/await:
Asynchronous Programming with Async and Await
Asynchrony in C# 5 Series (Eric Lippert)
Async Await Intro (Stephan Cleary)
Async Await FAQ

HttpClient call in asp.net

I am facing problem in calling HttpClient class and async call. I call the function List() from page_load. The calls return from the line HttpResponseMessage response = await client.GetAsync(str); and never comes back to finish it.
I don't understand what mistake I am doing. Following is my code:
protected void Page_Load(object sender, EventArgs e)
{
Task<string> s= List(product);
}
protected async Task<string> List(string ProductType)
{
string str = "http://xx.xx.com/wiki/api.php";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(String.Format("{0}:{1}", "username", "password"))));
HttpResponseMessage response = await client.GetAsync(str);
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
}
return content;
}
It never executes following lines.
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
Please help. Thanks in advance.
You never show what happens to the task in Page_Load, but given your symptoms and the fact that Page_Load is not async, I'm betting that you're calling Result or Wait instead of awaiting it.
When you do that, you cause a deadlock. The link to my blog explains this in detail, but the gist is that await will capture and restore the current "context". In ASP.NET, this is a request context, and the request context only allows one thread executing in it at a time. So, if you call Result or Wait (while you're in the request context), you'll block a thread in that request context until the Task<string> completes. Meanwhile, when the HttpClient gets its response, it tries to resume after the await client.GetAsync(str). But the request context will only allow one thread in at a time, and that thread is blocked waiting for the Task to complete. Since List cannot complete because the context is busy, you have a deadlock.
To prevent deadlocks, follow these two best practices (from my recent MSDN article):
Use async all the way. That is, use await instead of Result or Wait.
Use ConfigureContext(false) in your "library" code, i.e., every await in List should actually be await ... .ConfigureAwait(false);

Categories

Resources