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);
Related
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.
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.
I have a project where I need multiple data for a list of projects. For each project I call an api to get me this information. The loop works, although it takes 4 to 5 minutes to finish(Which is A LOT).
The code used to look like this :
foreach (var project in projects)
{
string url = urlOneProject + project.name + secondPartUrl + "?authtoken=" + authToken;
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
request.ContentType = "application/json";
var executions = new Execs();
var response = (HttpWebResponse)(await request.GetResponseAsync());
using (response)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
JavaScriptSerializer js = new JavaScriptSerializer();
var objText = reader.ReadToEnd();
executions = (Execs)js.Deserialize(objText, typeof(Execs));
}
}
execs.AddRange(executions.executions);
}
To improve performance I thought that using threads might be a good idea. So, I came up with something like this:
ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = projects.Count;
foreach (var project in projects)
{
new Thread(() =>
{
string url = urlOneProject + project.name + secondPartUrl + "?authtoken=" + authToken;
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
request.ContentType = "application/json";
var executions = new Execs();
var response = (HttpWebResponse)(await request.GetResponseAsync());
using (response)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
JavaScriptSerializer js = new JavaScriptSerializer();
var objText = reader.ReadToEnd();
executions = (Execs)js.Deserialize(objText, typeof(Execs));
}
}
lock (execs)
{
execs.AddRange(executions.executions);
}
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}).Start();
}
The problem with this code is that the line:
var response = (HttpWebResponse)(await request.GetResponseAsync());
doesn't compile anymore from the moment I added Thread. And the error I get is
"The 'await' operator can only be used within an async lambda expression "
That wasn't a problem when I didn't use threads. The GetResponseAsync is an async function and the use of the await is compulsory. I tried deleting it (which wasn't logical I agree but I ran out of options) but the compiler tells me I need an await for an async function.
I don't quite understand what changes with the implementation of the Thread.
Didn't I use the threads mechanically correctly? What should I do to correct this or to implement what I want to do correctly?
You are mixing multiple paradigms of coding, viz async / await, and oldschool Thread starting and synchronization, which is likely to lead to trouble.
As per the above comments
The reason your code doesn't compile is because you are attempting to use await in otherwise synchronous code passed to the thread. You can qualify lambdas as async as well.
Task is a much safer paradigm than Thread, and TPL provides rich and expressive tools to assist in asynchrony and parallelism.
If you process each parallel task in isolation, without sharing any data (such as the collections that you are locking), but instead return the resultant data from each Task you can then use LINQ to collate the results in a thread-safe manner.
var myTasks = projects.Select(async project =>
{
var url = $"urlOneProject{project.name}{secondPartUrl}?authtoken={authToken}";
var request = (HttpWebRequest) WebRequest.Create(url);
request.Accept = "application/json";
request.ContentType = "application/json";
using (var response = (HttpWebResponse) (await request.GetResponseAsync()))
using (var reader = new StreamReader(response.GetResponseStream()))
{
var objText = await reader.ReadToEndAsync();
return JsonConvert.DeserializeObject<Execs>(objText);
}
});
var execs = (await Task.WhenAll(myTasks))
.SelectMany(result => result.executions);
Other notes
Don't use JavaScriptSerializer - even the MSDN docco says to use NewtonSoft Json
There's an async version of reader.ReadToEndAsync which I've included.
You can drop the locks and the ManualResetEvent - since each Task returns it's result, we'll leave it to Task.WhenAll to collate the data.
You can flatten the children of multiple executions with a SelectMany
Adjacent using clauses are stackable - it saves a bit of eyestrain on the indentation.
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);
}
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.