GetStringAsync analogue for other methods (put, post, etc) - c#

Someone can tell me why for GET requests there is an option to get content out of the box via GetStringAsync. And for the other methods (POST, PUT...) there no such thing?
It's shorter to write a class to work with some API in the case of GET
HttpResponseMessage response = await client.GetAsync("random-url");
string content = await response.Content.ReadAsStringAsync();
SomeObject result = JsonSerializer.Deserialize<SomeObject>(content);
//shorter version
string content = await client.GetStringAsync("random-url");
SomeObject result = JsonSerializer.Deserialize<SomeObject>(content);
But in other cases, you have to write an additional line. I wouldn't say that this has a terrible effect on my performance, but the reason for the exclusion from the standard class is not clear

Related

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

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.

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.

How to get time to first byte with HttpClient class c#

Here's the code I am currently using to make calls to the api, where api is a string passed into the function. Set up of the httpClient is done beforehand outside of the method.
HttpResponseMessage response = await httpClient.GetAsync(api);
String strResp = await response.Content.ReadAsStringAsync();
httpClient.DefaultRequestHeaders.Accept.Clear();
Is there a way I can track metrics like time to first byte (TTFB) using this code, or will I have to go another route?
You should be able to use the HttpClient.GetAsync(string requestUri, HttpCompletionOption completionOption) method with HttpCompleteOption.ResponseHeadersRead. This should return the HttpResponseMessage as soon as the header is read (not exactly TTFB - "Time to first byte", but TTHR - "Time till header read")
Then you could measure this time using stopwached:
sw = new Stopwatch();
sw.Start();
HttpResponseMessage response = await httpClient.GetAsync(api);
sw.Stop();
Console.WriteLine("TTFB: " + sw.EleapsedMilliseconds)
String strResp = await response.Content.ReadAsStringAsync();
But this is also very unreliable due to the parallel nature of this code.
In this post someone advices to use WebClientand its DownloadProgressChanged in combination with a 'StopWatch'.
Ended up using FiddlerCore by Telerik to get TTFB and loads of other useful metrics.

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

C# Obtaining data from WebRequest not returning values

I have the following C# method that accepts a URL as an input and returns the text data that exists at that location:
public string GetWebData(string uri)
{
string response = string.Empty;
try
{
var request = WebRequest.Create(uri);
request.BeginGetResponse(result =>
{
var httpRequest = (HttpWebRequest)result.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(result);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
response = reader.ReadToEnd();
}
}, request);
}
catch (WebException)
{
response = string.Empty;
}
return response;
}
However, the reader.ReadToEnd(); method returns an empty string. I'm not sure if I'm doing anything wrong, since the method seems to be syntactically identical to all the tutorials I've consulted. What am I doing wrong?
You're returning response immediately - even though the callback which assigns a useful value to response will only fire later. Do you understand how BeginGetResponse works? It would be worth studying the documentation and examples carefully.
Why are you using asynchronous methods if you actually want to return the value as soon as the method finishes? If asynchronous methods are all you've got (e.g. you're on Windows Phone) then you should stick with that asynchronous idiom - don't try to make it synchronous.
As an aside, swallowing exceptions like this is a really bad idea too - you should almost never continue as if everything's okay, and even if you do want to ignore the error as far as the user experience is concerned, you should almost certainly be logging the exception.
Additionally, when you fetch the response you should use a using statement as WebResponse implements IDisposable.

Categories

Resources