ASP.NET WEB API do i need async client - c#

I have a Web API application which exposes certain async methods, such as:
public async Task<HttpResponseMessage> Put(string id){
var data = await Request.Content.ReadAsStringAsync();
//do something to put data in db
return Request.CreateResponse(HttpStatusCode.Ok);
}
I also have a class library that consumes this API with System.Net.WebRequest like this:
var request = (HttpWebRequest)WebRequest.Create(url);
var response = await request.GetResponseAsync();
Does the response need to be retrievend async? If so, why? Or can I also use request.GetResponse();.
I used the GetResponse before which would sometimes throw a 500 error ( An asynchronous module or handler completed while an asynchronous operation was still pending). Once I changed it to GetResponseAsync(), I stopped receiving this error.
EDIT: Code that sometimes throws a 500 error
I had the web api method stripped down to (just to check whether the 500 error was business logic or something else). Note: this is after changing the consumer to async (first function is the consumer, then the api method):
public HttpWebResponse(GetApiResponseForPut(string url, string putData, NameValueCollection headers)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.CookieContainer = _cookieContainer;
request.Method = "PUT";
if (headers != null)
{
request.Headers.Add(headers);
}
var encoding = new ASCIIEncoding();
var byte1 = new byte[0];
if (putData != null)
{
byte1 = encoding.GetBytes(putData);
}
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byte1.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(byte1, 0, byte1.Length);
requestStream.Close();
var response = await request.GetResponseAsync();
return (HttpWebResponse)response;
}
public async Task<HttpResponseMessage> Put(string id)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}

Does the response need to be retrievend async? If so, why?
No, it doesn't. You need to separate how the server-side operates and how the client queries your endpoint. When you expose a method which is async Task, you state that in your server-side calls, you're making async calls. This is transparent to the caller, all he gets is an API endpoint, he doesn't have any knowledge of your internal implementation.
Remember, even if you use async on the server-side, the request will only return to the caller once complete.

Related

Making a method async in Xamarin (C#)

I have this method that downloads data from the internet:
public ObservableCollection<Magnetka> ParseJSON() {
ObservableCollection<Models.Magnetka> Markers = new ObservableCollection<Models.Magnetka>();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
request.Headers.Add("Authentication-Token", "(KEY)");
request.Method = "GET";
request.ContentType = "application/json";
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
var response = request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
......... JSON parsing)
return Markers;
Now I found out this isn't too fast and it freezes the app for two seconds or so, so I need to make it async. However, I call the method like this in another class:
GetMarkers getMarkers = new GetMarkers();
Markers = getMarkers.ParseJSON();
How can I make my method async? I don't understand the concept well and if I make ParseJSON() async Task, I don't know where to put the "await" keyword.
Can anyone help me?
first, mark the method async and use the async version of GetResponse
public async Task<ObservableCollection<Magnetka>> ParseJSON()
{
...
var response = await request.GetResponseAsync();
...
return Markers;
}
then when you call it use the await keyword
Markers = await getMarkers.ParseJSON();
You would change your method to something like this instead:
public async Task<ObservableCollection<Magnetka>> ParseJSON()
{
var markers = new ObservableCollection<Magnetka>();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
request.Headers.Add("Authentication-Token", "(KEY)");
request.Method = "GET";
request.ContentType = "application/json";
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
using var response = await request.GetResponseAsync().ConfigureAwait(false);
using var responseStream = response.GetResponseStream();
using var streamReader = new StreamReader(responseStream);
var json = await streamReader.ReadToEndAsync().ConfigureAwait(false);
// json parsing
return markers;
}
Firstly, you would mark your method async. This signals the compiler to set up a state machine for this method for continuation. Nothing async happening yet though.
Another thing you need to do is to wrap your return value in a Task, which is similar to a promise in some other languages. Tasks are await-able, which is where the asynchronous stuff is happening.
Switching from using the synchronous methods in WebRequest to use the Async variants and awaiting their results makes your method async.
So from:
request.GetResponse()
To:
await request.GetResponseAsync()
Which returns a awaitable Task<WebResponse>.
The same goes for the stream reader code, you can use its async API too.
A tricky thing by using asynchronous code is that, it kind of forces you to do this all the way up. However, there are exceptions for lifecycle methods and events, where you don't necessarily have to change the signature to return a Task.
Another thing you might notice here. I've added ConfigureAwait(false) at the end of the async method calls. I've done this because out of the box, after returning from an awaited method, the code will try to return to the originating context. This can lead to a couple of issues such as dead-locks if the originating context is the UI Thread. Or, it can lead to bad performance trying to switch between context a lot. Adding ConfigureAwait(false) will just return on the same context the await was run on, which is fine as long as we don't need or expect to return on that context.
As for where you call ParseJson() is probably equally important. Consider deferring it to some lifecycle method or event where you are not doing much else.
This could be OnResume() on Android or ViewWillAppear on iOS. If you are using MVVM, use a Command to encapsulate it.

Android UI freeze while waiting for a http response

When i'm sending a http request with my app, the UI of my app freezes while i'm waiting for the http response.
Why is this happening?
async void HttpAction()
{
var request = HttpWebRequest.Create(url);
request.Method = "GET";
request.Timeout = 1500;
await Task.Delay(500);
using (HttpWebResponse reponse = request.GetResponse() as HttpWebResponse)
{
if (reponse.StatusCode == HttpStatusCode.OK)
{
//UI action
reponse.Close();
}
}
Thanks a lot!
You need to await the HttpAction method at the calling point. Go through this Async/Await article first for better understanding of Asynchronous programming.
There should not be any UI action inside Asynchronous Task rather return the result to the calling point and then perform the necessary actions.
async Task<bool> GetHttpAsyncStatus()
{
var request = HttpWebRequest.Create(url);
request.Method = "GET";
request.Timeout = 1500;
using (HttpWebResponse reponse = await request.GetResponseAsync() as HttpWebResponse)
{
if (reponse.StatusCode == HttpStatusCode.OK)
{
// no UI Action, just return the result
reponse.Close();
return true;
}
return false;
}
}
This must be invoked as
bool requestStatus = await GetHttpAsyncStatus();
You need to use AsyncTask and run your http calls in the doInBackground method.
And inside the onPostExecute method, you update the ui. AsyncTask is not terribly involved and will free up the ui thread. I don't have much experience with async but AsyncTask and HttpurlConnection has never failed me on the more tricky stuff.

How to collect the returned value of an awaited method in a variable in another method

So I am trying to grab the source code from a url and here is my code:
public async Task<string> grabPageHtml(Uri pageUrl)
{
var request = WebRequest.Create(pageUrl) as HttpWebRequest;
request.Method = "GET";
WebResponse responseObject = await Task<WebResponse>.Factory.FromAsync(
request.BeginGetResponse,
request.EndGetResponse, request);
var responseStream = responseObject.GetResponseStream();
var sr = new StreamReader(responseStream);
received = sr.ReadToEndAsync().Result;
return received;
}
The code works correctly and returns the sourcecode. The problem is assigning "received" to a string variable in another method.
Tried this:
string pageHtml = grabPageHtml(pageUrl)
but pageHtml outputs System.Threading.Tasks.Task'1[System.String] to the console instead of the source code.
pageHtml outputs System.Threading.Tasks.Task1[System.String]` to the
console instead of the source code.
That's because your method returns a Task<string>, not a string. You need to extract that string result somehow. It should be:
public async Task FooAsync()
{
string pageHtml = await GrabPageHtmlAsync(pageUrl);
Console.WriteLine(pageHtml);
}
As a side note, don't block on async code. Using Task.Result may lead to deadlocks. Also, using HttpClient would make your code a bit cleaner, removing the need to call FromAsync:
public Task<string> GrabPageHtmlAsync(Uri pageUrl)
{
var httpClient = new HttpClient();
return httpClient.GetStringAsync(pageUrl);
}

Issue with async Task<T> in xamarin forms

I'm trying to use the Azure media services with REST api, in a xamarin shared project on visual studio 2013. This is the code i use to get the access token.
public HttpFactory()
{
string token = GetToken(serviceURI).Result;
//some more methods also async tasks
}
private async Task<string> GetToken(Uri serviceUri)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceURI);
request.Accept = "application/json";
request.Method = "GET";
request.Headers["Host"] = "media.windows.net";
request.Headers["x-ms-version"] = "2.9";
request.Headers["Authorization"] = "Bearer " + token;
var response = (HttpWebResponse) await request.GetResponseAsync();
if (response.StatusCode == HttpStatusCode.MovedPermanently)
{
serviceURI = new Uri(response.Headers["Location"]);
HttpWebRequest req = ( HttpWebRequest)WebRequest.Create(serviceURI);
var res = (HttpWebResponse)await req.GetResponseAsync();
using (Stream responseStream = res.GetResponseStream())
{
using (StreamReader reader = new StreamReader(responseStream))
{
string str = reader.ReadToEnd();
// var test = JsonConvert.DeserializeObject(str);
JToken jtoken = JsonConvert.DeserializeObject<JToken>(str);
return jtoken["access_token"].Value<string>();
}
}
}
return "";
}
But when the compiler reaches -
var response = (HttpWebResponse) await request.GetResponseAsync();
it skips the rest of the code, and i never get the response. I know the code is working - because it works just fine without the task, in a async void method.
Anyone knows how to fix this, or am i doing something wrong? I also tried this in vs2015 but its the same.
You have a deadlock over the UI thread.
You're blocking the thread with Task.Result when it is needed to complete the async method which will complete the task that it's waiting on.
That's why you shouldn't block synchronously on asynchronous code. You should await the task returned from GetToken instead:
string token = await GetToken(serviceURI);
If you can't use async in that method, either move that logic to a different method (e.g. OnLoad event handler).
Another solution would be to use ConfigureAwait on the GetResponseAsync task and so the rest of the method wouldn't run on the UI thread, avoiding the deadlock:
var response = (HttpWebResponse) await request.GetResponseAsync().ConfigureAwait(false);

HttpWebRequest BeginGetRequestStream callback never called

In my Xamarin application I use HttpWebRequest class to send POST messages to the server (I use it because it is available out-of-the box in PCL libraries).
Here is some request preparation code:
request.BeginGetRequestStream (asyncResult => {
Mvx.Trace ("BeginGetRequestStream callback");
request = (HttpWebRequest)asyncResult.AsyncState;
Stream postStream = request.EndGetRequestStream (asyncResult);
string postData = jsonConverter.SerializeObject (objectToSend);
Mvx.Trace ("Posting following JSON: {0}", postData);
byte[] byteArray = Encoding.UTF8.GetBytes (postData);
postStream.Write (byteArray, 0, byteArray.Length);
MakeRequest (request, timeoutMilliseconds, successAction, errorAction);
}, request);
When I start application and execute this code for the first and the second time everything works fine. But when this is executed for the 3rd time (exactly!) the callback is not called and line "BeginGetRequestStream callback" is never printed to log. Is it a bug in class implementation or maybe I do something incorrectly?
If it is not possible to make this working in Xamarin please suggest reliable and convenient class for sending Http GET and POST request with timeout.
Also created related, more general question: Sending Http requests from Xamarin Portable Class Library
My solution to send and receive messages JSON in Xamarin PCL:
public async Task<string> SendMessageJSON(string message, string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.ContentType = "application/json";
request.Method = "POST";
// Send data to server
IAsyncResult resultRequest = request.BeginGetRequestStream(null, null);
resultRequest.AsyncWaitHandle.WaitOne(30000); // 30 seconds for timeout
Stream streamInput = request.EndGetRequestStream(resultRequest);
byte[] byteArray = Encoding.UTF8.GetBytes(message);
await streamInput.WriteAsync(byteArray, 0, byteArray.Length);
await streamInput.FlushAsync();
// Receive data from server
IAsyncResult resultResponse = request.BeginGetResponse(null, null);
resultResponse.AsyncWaitHandle.WaitOne(30000); // 30 seconds for timeout
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(resultResponse);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string result = await streamRead.ReadToEndAsync();
await streamResponse.FlushAsync();
return result;
}
Finally solved this by switching to Profile 78 and HttpClient, which works well in all cases.

Categories