How to use HttpWebRequest (.NET) asynchronously?
Use HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
The callback function is called when the asynchronous operation is complete. You need to at least call EndGetResponse()
from this function.
Async HttpWebRequest call using C#
I suppose the best way for making an async
request is HttpClient
.
You can write something like this:
var client = new HttpClient();
var request = new HttpRequestMessage()
{
RequestUri = new Uri("xxxxxx"),
Method = HttpMethod.Get,
};
request.Headers.Accept.Add(new ....);
var response = await client.SendAsync(request);
//read a result from the repsonse
How to make HttpWebRequest async
The call to Result
in your foreach
loop is causing a deadlock, as I explain on my blog. In summary, await
will capture a "context" (e.g., a UI context), and use that to resume the async
method. Some contexts (e.g., the UI context) only allow one thread in the context. So if you block that special thread (e.g., the UI thread) by calling Result
, then the async
method cannot resume execution within that context.
So, the solution is to change your foreach
loop:
foreach(var url in myUrlList)
{
string body = await ProcessAsync(method);
}
Other notes:
Task-returning methods should end in "Async" to follow the TAP guidelines.
Task.Factory.FromAsync
is unnecessary; HttpWebRequest
already has awaitable methods. An even better option is to use HttpClient
instead.
I recommend that you not use Task.ContinueWith
(or Task.Result
, or Task.Wait
); use await
instead.
With these simplifications in place:
private async Task<string> MakeAsyncRequestAsync(string url, string contentType)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = contentType;
request.Method = WebRequestMethods.Http.Get;
request.Timeout = 20000;
request.Proxy = null;
WebResponse response = await request.GetResponseAsync();
return ReadStreamFromResponse(response);
}
This code could be simplified further if you change HttpWebRequest
to HttpClient
.
How to make a HttpWebRequest function Async?
HttpClient client = new HttpClient();
public async Task PostAsync(string actionName, object json)
{
var content = new StringContent(json.ToString(), Encoding.UTF8,"application/json");
var resultRoles = await client.PostAsync(new Uri(actionName),content);
}
You're new so I wont go into detail about HttpClient, but the instance should be reused. Try to do some self research on how this should be accomplished.
Implementing HttpWebRequest Async calls
The documentation has a good example (http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetrequeststream(v=vs.100).aspx):
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Threading;
class HttpWebRequestBeginGetRequest
{
private static ManualResetEvent allDone = new ManualResetEvent(false);
public static void Main(string[] args)
{
// Create a new HttpWebRequest object.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.contoso.com/example.aspx");
request.ContentType = "application/x-www-form-urlencoded";
// Set the Method property to 'POST' to post data to the URI.
request.Method = "POST";
// start the asynchronous operation
request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);
// Keep the main thread from continuing while the asynchronous
// operation completes. A real world application
// could do something useful such as updating its user interface.
allDone.WaitOne();
}
private static void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
Stream postStream = request.EndGetRequestStream(asynchronousResult);
Console.WriteLine("Please enter the input data to be posted:");
string postData = Console.ReadLine();
// Convert the string into a byte array.
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Write to the request stream.
postStream.Write(byteArray, 0, postData.Length);
postStream.Close();
// Start the asynchronous operation to get the response
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
private static void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
Console.WriteLine(responseString);
// Close the stream object
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse
response.Close();
allDone.Set();
}
HttpWebRequest async versus Begin/End
the controller is marked async but the implementation uses synchronous HttpWebRequest calls, in that my controller is awaiting Task.Run( sync web request )
Think about what is happening in the request. The request comes in and ASP.NET takes a thread pool thread to handle the request. The controller action queues the work to the thread pool (taking up another thread), and then await
s that work, freeing up the original request thread. You haven't gained anything by using await
because there's still a thread pool thread blocking on the web request.
For this reason, you should almost never use Task.Run
(or any other method that queues work to the thread pool) on ASP.NET.
Is this suitable or should i use the Begin/End methods of HttpWebRequest too. This seems like overkill as it would be asyncing an already async operation
It's not really asynchronous right now; there's still a thread being blocked. I call this "queue blocking work to a thread pool thread and then await it" technique fake asynchrony.
The appropriate fix is to use HttpClient
, which was designed for asynchronous use; or, you could use TaskFactory<T>.FromAsync
to wrap the Begin
/End
methods into an awaitable task.
The thing i dont understand though is if i use await HttpClient.SendAsync then somewhere something must be blocking waiting for a response
No, there doesn't have to be something blocking somewhere. As I describe on my blog, in a truly asynchronous scenario (i.e., not fake-asynchrony), there is no thread.
How can I use System.Net.HttpWebRequest with async/await?
If you use a generic Task<>.Factory
you get a little more type-safety:
var request = WebRequest.CreateHttp("https://www.google.com");
object state = null; // or whatever state you want.
var task = Task<WebResponse>.Factory.FromAsync(
request.BeginGetResponse,
request.EndGetResponse,
state);
However, as with when you're not doing async
calls, if you want an HttpWebResponse
rather than just a WebResponse
, you'll need to do an additional cast. And be sure to close/dispose your response:
using (var response = (HttpWebResponse) (await task))
{
// use response
}
WebRequest async asp.net-mvc
sorry, I didn't quite understand that I didn't insert my attempts to use async, I did it, and it seems to work, can I improve something in my code? added 2 lines
public async Task<IEnumerable<JiraDataModel>> GetData(string dateFrom, string dateTo)
{
int allTicketsCount = 0;
int devTicketsCount = 0;
int slaTicketsCount = 0;
List<JiraRequestUrl> urlArray = new List<JiraRequestUrl>();
urlArray.AddRange(new List<JiraRequestUrl>
{
new JiraRequestUrl{type = "all", //*data*//},
new JiraRequestUrl(){type = "dev",//*data*//});
try
{
foreach (JiraRequestUrl u in urlArray)
{
WebRequest request = WebRequest.Create(_jiraUrl + u.url);
request.ContentType = "application/json; charset=utf-8";
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes(_credentials)));
request.Headers.Add("maxResults", "100");
HttpWebResponse response = (HttpWebResponse)(await request.GetResponseAsync()request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
JiraGetDataModel.RootObject DeserializedResponse = (JiraGetDataModel.RootObject)JsonConvert.DeserializeObject(responseString, typeof(JiraGetDataModel.RootObject));
if (u.type.Equals("all"))
{
allTicketsCount = DeserializedResponse.total;
}
if (u.type.Equals("dev"))
{
devTicketsCount = DeserializedResponse.total;
}
}
}
Related Topics
Convert Ienumerable to Datatable
Assign Bitmapimage from Resources.Resx to Image.Source
How to Find the Number of Cpu Cores Via .Net/C#
.Net Httpclient. How to Post String Value
Calculate the Number of Business Days Between Two Dates
Asp.Net MVC: Custom Validation by Dataannotation
Reflection: How to Invoke Method With Parameters
Getting All Types in a Namespace Via Reflection
Sort a List from Another List Ids
Conditionally Change CSS Class in Razor View
My Algorithm to Calculate Position of Smartphone - Gps and Sensors
How to Run Servicestack on Linux/Mono
Cross-Platform File Name Handling in .Net Core