How can I use HttpWebRequest (.NET, C#) 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.
By far the easiest way is by using TaskFactory.FromAsync from the TPL. It's literally a couple of lines of code when used in conjunction with the new async/await keywords:
var request = WebRequest.Create("http://www.stackoverflow.com");
var response = (HttpWebResponse) await Task.Factory
.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null);
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
If you can't use the C#5 compiler then the above can be accomplished using the Task.ContinueWith method:
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Considering the answer:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
You could send the request pointer or any other object like this:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Greetings
Everyone so far has been wrong, because BeginGetResponse() does some work on the current thread. From the documentation:
The BeginGetResponse method requires some synchronous setup tasks to
complete (DNS resolution, proxy detection, and TCP socket connection,
for example) before this method becomes asynchronous. As a result,
this method should never be called on a user interface (UI) thread
because it might take considerable time (up to several minutes
depending on network settings) to complete the initial synchronous
setup tasks before an exception for an error is thrown or the method
succeeds.
So to do this right:
void DoWithResponse(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
var response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
You can then do what you need to with the response. For example:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
public static async Task<byte[]> GetBytesAsync(string url) {
var request = (HttpWebRequest)WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var content = new MemoryStream())
using (var responseStream = response.GetResponseStream()) {
await responseStream.CopyToAsync(content);
return content.ToArray();
}
}
public static async Task<string> GetStringAsync(string url) {
var bytes = await GetBytesAsync(url);
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
I ended up using BackgroundWorker, it is definitely asynchronous unlike some of the above solutions, it handles returning to the GUI thread for you, and it is very easy to understand.
It is also very easy to handle exceptions, as they end up in the RunWorkerCompleted method, but make sure you read this: Unhandled exceptions in BackgroundWorker
I used WebClient but obviously you could use HttpWebRequest.GetResponse if you wanted.
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) => {
args.Result = new WebClient().DownloadString(settings.test_url);
};
worker.RunWorkerCompleted += (sender, e) => {
if (e.Error != null) {
connectivityLabel.Text = "Error: " + e.Error.Message;
} else {
connectivityLabel.Text = "Connectivity OK";
Log.d("result:" + e.Result);
}
};
connectivityLabel.Text = "Testing Connectivity";
worker.RunWorkerAsync();
.NET has changed since many of these answers were posted, and I'd like to provide a more up-to-date answer. Use an async method to start a Task that will run on a background thread:
private async Task<String> MakeRequestAsync(String url)
{
String responseText = await Task.Run(() =>
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
WebResponse response = request.GetResponse();
Stream responseStream = response.GetResponseStream();
return new StreamReader(responseStream).ReadToEnd();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
return null;
});
return responseText;
}
To use the async method:
String response = await MakeRequestAsync("http://example.com/");
Update:
This solution does not work for UWP apps which use WebRequest.GetResponseAsync() instead of WebRequest.GetResponse(), and it does not call the Dispose() methods where appropriate. #dragansr has a good alternative solution that addresses these issues.
public void GetResponseAsync (HttpWebRequest request, Action<HttpWebResponse> gotResponse)
{
if (request != null) {
request.BeginGetRequestStream ((r) => {
try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash
HttpWebResponse response = request.EndGetResponse (r);
if (gotResponse != null)
gotResponse (response);
} catch (Exception x) {
Console.WriteLine ("Unable to get response for '" + request.RequestUri + "' Err: " + x);
}
}, null);
}
}
Follow up to the #Isak 's answer, which is very good. Nonetheless it's biggest flaw is that it will only call the responseAction if the response has status 200-299. The best way to fix this is:
private void DoWithResponseAsync(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
HttpWebResponse response;
try
{
response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
}
catch (WebException ex)
{
// It needs to be done like this in order to read responses with error status:
response = ex.Response as HttpWebResponse;
}
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
And then as #Isak follows:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
I've been using this for async UWR, hopefully it helps someone
string uri = "http://some.place.online";
using (UnityWebRequest uwr = UnityWebRequest.Get(uri))
{
var asyncOp = uwr.SendWebRequest();
while (asyncOp.isDone == false) await Task.Delay(1000 / 30); // 30 hertz
if(uwr.result == UnityWebRequest.Result.Success) return uwr.downloadHandler.text;
Debug.LogError(uwr.error);
}
Related
This is my method, to which I am passing the url to check if it's active.
The link is being activated on the wowza service so it takes some time until it's "alive"
GetResponse is returning the 404 Error because the url is not reached.
Is there a way to get the timeout instead of 404 error if the url is not alive after specified time?
public async Task<IActionResult> GetLinkIsAlive([FromQuery] string url, [FromQuery] int timeout)
{
HttpWebResponse webResponse;
try
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Timeout = timeout;
webRequest.Method = "GET";
webResponse = webRequest.GetResponse() as HttpWebResponse;
return Ok(webResponse.StatusCode);
}
catch (WebException ex)
{
return BadRequest(ex.Message);
}
}
You can use connection pooling.
It’s using IHttpClientFactory that helps to maintain the pooling and lifetime of clients
In your startup class :
services.AddHttpClient<NameofyourService, NameofyourService>()
.AddTransientHttpErrorPolicy(
p => p.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}));
It requires Microsoft.Extensions.Http.Polly
You need to use HttpClient for your service. All added configurations will apply automatically.
My solution to this was calling the link in the while loop every 1s and await the whole Task.
private async Task<bool> IsLiveStreamAlive(string streamUrl, int retriesCount = 30)
{
try
{
bool res = false;
HttpClient client = new HttpClient();
Uri uri = new Uri(streamUrl);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
var request = new HttpRequestMessage(HttpMethod.Get, uri);
while (retriesCount > 1)
{
await Task.Delay(1000);
retriesCount--;
HttpResponseMessage httpResponse = await client.GetAsync(uri);
res = httpResponse.StatusCode == HttpStatusCode.OK ? true : false;
if (res)
{
Log.Info(string.Format("Stream alive: {0}", streamUrl));
break;
}
}
return res;
}
catch (WebException ex)
{
Log.Error(ex);
}
return false;
}
I have an asp.net web API. (net framework 4.6.1) I am calling a 3rd party rest API (Product) in one of my actions. In functional tests, everything works as expected, no problem. But when I do spike tests (let's say, 100 users, 10 seconds ramp-up time) I am getting System.Threading.Tasks.TaskCanceledException: A task was canceled error after some successful responses. I googled and came across that it might be due to timeout. I added TimeSpan.FromSeconds(600); but still getting the same error.
Here is how I call this 3rd party API. Is there any problem with an async call? Would you please have a look at my code?
public class UtilitiesTest
{
private static readonly HttpClient _httpClient = new HttpClient();
//some not relevant code here
public static async Task<HttpResponseMessage> CallRazer(GameRequest gameRequest, string url)
{
try
{
FormUrlEncodedContent content = null;
if (url == "Product/")
{
try
{
//some code here
//Timeout
_httpClient.Timeout = TimeSpan.FromSeconds(600);
//Call Game
var response = await _httpClient.PostAsync("https://test.com/" + url, content);
return response;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
content.Dispose();
}
}
else
{
try
{
//some code here
//Call Game
var response = await _httpClient.PostAsync("https://test.com/" + url, content);
return response;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
content.Dispose();
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
and here is how I call Razer:
private async Task<HttpResponseMessage> CallProducts()
{
//some code here
#region Call Razer for products
//**Call Razer**
var response = await Utilities.CallRazer(products, "Product/");
var htmlResponse = await response.Content.ReadAsStringAsync();
var model = JsonConvert.DeserializeObject<ProductResponseDto>(htmlResponse);
// some code here
return response;
}
Somebody in another forum, suggested me to use HTTPWeb Request instead of Httpclient. So I changed my code like below and all my troubles are gone.
//HTTPWebRequest
var request = (HttpWebRequest) WebRequest.Create("http://test.com" + url);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
var keyValueContent = productRequest.ToKeyValue();
var formUrlEncodedContent = new FormUrlEncodedContent(keyValueContent);
var urlEncodedString = await formUrlEncodedContent.ReadAsStringAsync();
using (var streamWriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
streamWriter.Write(urlEncodedString);
}
HttpWebResponse httpResponse = (HttpWebResponse) (await request.GetResponseAsync());
response = new HttpResponseMessage
{
StatusCode = httpResponse.StatusCode,
Content = new StreamContent(httpResponse.GetResponseStream()),
};
return response;
If it's still timing out after 600 seconds, then your web service just can't keep up.
But yes, a timeout will generate a TaskCanceledException, which is not intuitive at all. This will actually be changing in .NET Core at least (just fixed last week), but that doesn't help you.
Here is code I've used to rethrow a TimeoutException instead. The only other reason a TaskCanceledException could be thrown is if you passed a CancellationToken that ended up being cancelled, but you're not. So it's definitely a timeout.
} catch (TaskCanceledException) {
//Could have been caused by cancellation or timeout if you used one.
//If that was the case, rethrow.
//cancellationToken.ThrowIfCancellationRequested();
//HttpClient throws TaskCanceledException when the request times out. That's dumb.
//Throw TimeoutException instead and say how long we waited.
string time;
if (_httpClient.Timeout.TotalHours > 1) {
time = $"{_httpClient.Timeout.TotalHours:N1} hours";
} else if (_httpClient.Timeout.TotalMinutes > 1) {
time = $"{_httpClient.Timeout.TotalMinutes:N1} minutes";
} else if (_httpClient.Timeout.TotalSeconds > 1) {
time = $"{_httpClient.Timeout.TotalSeconds:N1} seconds";
} else {
time = $"{_httpClient.Timeout.TotalMilliseconds:N0} milliseconds";
}
throw new TimeoutException($"No response after waiting {time}.");
}
I'm trying to upload data to an external webservice, i'm using the WebClients UploadDataTaskAsync to do this asynchronously. but it never returns. I've tested this using the synchronous call and it will timeout eventually.
public Notification Notify(Notification notification)
{
var messageXml = MessageFactory.Create(notification);
var statusCode = SendWebRequestAsync(messageXml);
notification.ProcessedAttempts++;
if (statusCode.Result == HttpStatusCode.OK)
{
notification.Processed = true;
notification.DateProcessed = DateTime.UtcNow;
}
return notification;
}
private async Task<HttpStatusCode> SendWebRequestAsync(string message)
{
using (var webClient = new WebClient())
{
byte[] data = message.ToUtf8Bytes();
var uri = new Uri(url);
try
{
webClient.UploadDataCompleted += WebClient_UploadDataCompleted;
var result = await webClient.UploadDataTaskAsync(uri, "POST", data);
string response = Encoding.UTF8.GetString(result);
if (response == "")
return HttpStatusCode.OK;
return HttpStatusCode.NoContent;
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var response = ex.Response as HttpWebResponse;
if (response != null)
{
return response.StatusCode;
}
}
// no http status code available
return HttpStatusCode.ServiceUnavailable; // guess
}
catch (Exception)
{
return HttpStatusCode.InternalServerError;
}
}
}
private void WebClient_UploadDataCompleted(object sender, UploadDataCompletedEventArgs e)
{
Console.WriteLine(e.Result);
}
You're calling statusCode.Result which is the classic ASP.NET deadlock. This makes no sense because you're apparently using async IO to get scalability benefits and then destroying that by blocking.
Don't block.
Also, it looks like HttpClient could reduce the code that you have there a bit.
I have a web service where I Post to a URL Asynchronously
public Response uploadXMLData(string destinationUrl, string requestXml,Request req)
{
try
{
Response resp=new Response();
System.Uri uri = new System.Uri(destinationUrl);
using (WebClient client = new WebClient())
{
client.UploadStringCompleted
+= new UploadStringCompletedEventHandler(UploadStringCallback);
client.UploadStringAsync(uri, "POST",requestXml,req);
}
}
catch (Exception ex)
{}
return resp;
}
public void UploadStringCallback(object sender, UploadStringCompletedEventArgs e)
{
Response resp=new Response();
Request req = new Request();
try
{
if (e.Error != null)
{
object objException = e.Error.GetBaseException();
Type _type = typeof(WebException);
if (_type != null)
{
WebException objErr = (WebException)e.Error.GetBaseException();
WebResponse rsp = objErr.Response;
using (Stream respStream = rsp.GetResponseStream())
{
req= (Request)e.UserState;
resp=Utilities.ParseWithoutSoapEnv(respStream);
}
}
else
{
Exception objErr = (Exception)e.Error.GetBaseException();
throw objErr;
}
}
else
{
//Parse e.Result
}
}
catch (Exception ex)
{}
}
The Function Called Utilities.ParseWithoutSoapEnv(respStream); returns Type Response
what I want to do is get the Response from that function, and make it the return value for uploadXMLData
But I can't change the return type of a CallBack Function, so I have no idea what to do.
When someone calls my webservice function, they expect it to return a type of Response, and the Response class I need is being received to the CallBack function..
Hope I made my issue clear
Any help would be appreciated
Things get a little easier if you use the Task Asynchronous Pattern and HttpClient. You'll need to modify your method to return a Task<Response>:
public async Task<Response> UploadXmlDataAsync(
string destinationUrl,
string requestXml,
Request req)
{
try
{
Response resp=new Response();
System.Uri uri = new System.Uri(destinationUrl);
var httpClient = new HttpClient();
var response = await httpClient.PostAsync(Uri, new StringContent(requestxml))
.ConfigureAwait(false);
var responeStream = response.Content.ReadAsStreamAsync().ConfigureAwait(false);
return Utilities.ParseWithoutSoapEnv(responseStream);
}
}
And when you call it higher up the call-stack, you'll need to await that too:
public async Task FooAsync()
{
var parsedResponse = await UploadXmlDataAsync(url, xml, request);
// Do something with response.
}
If you want a synchronous alternative, you can also do:
public Response UploadXmlData(string destinationUrl, string requestXml,Request req)
{
Response resp=new Response();
System.Uri uri = new System.Uri(destinationUrl);
using (WebClient client = new WebClient())
{
var response = client.UploadString(uri, "POST", requestXml, req);
using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(response))
{
return Utilities.ParseWithoutSoapEnv(responseStream);
}
}
}
I have the following code:
private 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();
string postData = "my data";
// 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
IAsyncResult result =
(IAsyncResult)request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
private 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();
Dispatcher.BeginInvoke((Action)(() => Debug.WriteLine("George")));
}
However when my code hits BeginGetResponse it never exits (and I do not hit a breakpoint in the GetResponseCallback function). I tried adding the BeginInvoke call, but I still never enter this method. This code works in a windows console app - it's on Windows Phone 7 that it doesn'teorg
Can anyone see what I am doing wrong?
Thanks.
If you have created the HttpWebRequest on the UI thread, then make sure you don't block the UI thread, otherwise you can deadlock.
The sample from the desktop .NET you have linked isn't optimized for the current phone networking stack. You should change the code so that you create the HttpWebRequest on a background thread.
I can't see what's wrong with your code (maybe a complete example of what you're trying to do may help) but here's a simple working example of a way of performing the action you want to do.
It posts some data to a URI and then passes the repsonse to a callback function:
Simply execute like this (use of a BackgroundWorker is not necessary but is recommended)
var bw = new BackgroundWorker();
bw.DoWork += (o, args) => PostDataToWebService("http://example.com/something", "key=value&key2=value2", MyCallback);
bw.RunWorkerAsync();
Here's the callback function it refers to:
(You can change this however is appropriate to your needs.)
public static void MyCallback(string aString, Exception e)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (e == null)
{
// aString is the response from the web server
MessageBox.Show(aString, "success", MessageBoxButton.OK);
}
else
{
MessageBox.Show(e.Message, "error", MessageBoxButton.OK);
}
});
}
Here's the actual method:
public void PostDataToWebService(string url, string data, Action<string, Exception> callback)
{
if (callback == null)
{
throw new Exception("callback may not be null");
}
try
{
var uri = new Uri(url, UriKind.Absolute);
var req = HttpWebRequest.CreateHttp(uri);
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
AsyncCallback GetTheResponse = ar =>
{
try
{
var result = ar.GetResponseAsString();
callback(result, null);
}
catch (Exception ex)
{
callback(null, ex);
}
};
AsyncCallback SetTheBodyOfTheRequest = ar =>
{
var request = ar.SetRequestBody(data);
request.BeginGetResponse(GetTheResponse, request);
};
req.BeginGetRequestStream(SetTheBodyOfTheRequest, req);
}
catch (Exception ex)
{
callback(null, ex);
}
}
and here are the extension/helper methods it uses:
public static class IAsyncResultExtensions
{
public static string GetResponseAsString(this IAsyncResult asyncResult)
{
string responseString;
var request = (HttpWebRequest)asyncResult.AsyncState;
using (var resp = (HttpWebResponse)request.EndGetResponse(asyncResult))
{
using (var streamResponse = resp.GetResponseStream())
{
using (var streamRead = new StreamReader(streamResponse))
{
responseString = streamRead.ReadToEnd();
}
}
}
return responseString;
}
public static HttpWebRequest SetRequestBody(this IAsyncResult asyncResult, string body)
{
var request = (HttpWebRequest)asyncResult.AsyncState;
using (var postStream = request.EndGetRequestStream(asyncResult))
{
using (var memStream = new MemoryStream())
{
var content = body;
var bytes = System.Text.Encoding.UTF8.GetBytes(content);
memStream.Write(bytes, 0, bytes.Length);
memStream.Position = 0;
var tempBuffer = new byte[memStream.Length];
memStream.Read(tempBuffer, 0, tempBuffer.Length);
postStream.Write(tempBuffer, 0, tempBuffer.Length);
}
}
return request;
}
}