I would like to know which approach among the two is a better implementation ?
I need to create a web request which can range between 200ms to 5 seconds. I need the html response to proceed - so need to block on the main thread.
First Approach
string GetResponse()
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
IAsyncResult result = request.BeginGetResponse(null, null);
using (HttpWebResponse httpResponse = (HttpWebResponse)request.EndGetResponse(result))
{
using (Stream dataStream = httpResponse.GetResponseStream())
{
StreamReader reader = new StreamReader(dataStream);
response = reader.ReadToEnd();
}
}
Second Approach
string response = string.Empty;
AutoResetEvent waitHandle = null;
void GetResponse(string url)
{
waitHandle = new AutoResetEvent(false);
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
IAsyncResult asyncResult = request.BeginGetResponse(Callback, request);
waitHandle.WaitOne();
}
catch { }
finally
{
waitHandle.Close();
}
}
void Callback(IAsyncResult asyncResult)
{
HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
try
{
using (HttpWebResponse httpResponse = (HttpWebResponse)request.EndGetResponse(asyncResult))
{
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
using (Stream dataStream = httpResponse.GetResponseStream())
{
StreamReader reader = new StreamReader(dataStream);
response = reader.ReadToEnd();
}
}
}
}
catch { }
finally
{
waitHandle.Set();
}
}
Why not execute the web request on the main thread? If you want the main thread to block, this is by far the easiest way to accomplish this.
Related
I have a HttpListener which is waiting for incoming requests. The curious thing is that when I send a request with the HttpClient or WebRequest class, the reading/decoding of the stream takes around 350ms, while sending the same request with Insomnia (https://insomnia.rest/) it only takes 500ticks!!
Can someone explain to me where is my fault?!
HttpClient
private readonly HttpClient _Client = new HttpClient();
private async void HttpClientMethod(string jsonContent)
{
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, _BaseUrl);
message.Content = new StringContent(jsonContent);
HttpResponseMessage result = await _Client.SendAsync(message);
string content = await result.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
WebRequest Client
private string WebRequestMethod(string jsonContent)
{
WebRequest request = WebRequest.Create(_BaseUrl);
// Set the Method property of the request to POST.
request.Method = "POST";
// Create POST data and convert it to a byte array.
string postData = jsonContent;
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Set the ContentType property of the WebRequest.
request.ContentType = "application/x-www-form-urlencoded";
// Set the ContentLength property of the WebRequest.
request.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream();
// Write the data to the request stream.
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close();
// Get the response.
WebResponse response = request.GetResponse();
// Display the status.
Console.WriteLine(((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
// Clean up the streams.
reader.Close();
dataStream.Close();
response.Close();
return responseFromServer;
}
Host
private void ReadInputStream(HttpListenerRequest request)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string text;
using (StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
text = reader.ReadToEnd();
}
stopwatch.Stop();
}
HttpClient | WebRequest
Insomnia
Insonmia Body | JSONContent
Sample Project
Just create a new Console App
Program.cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace ApiTest
{
class Program
{
static readonly ApiHost _ApiHost = new ApiHost();
static readonly HttpClient _Client = new HttpClient();
static void Main(string[] args) => MainAsync(args).GetAwaiter().GetResult();
static async Task MainAsync(string[] args)
{
_ApiHost.Start();
Console.WriteLine("Host started");
Console.ReadKey();
string jsonContent ="{\"OperationName\":null,\"Query\":\"query {\\r\\n\\t hero{\\r\\n\\t\\t name,\\r\\n\\t\\t id\\r\\n\\t }\\r\\n\\t human(id: \\\"1\\\"){\\r\\n\\r\\n homePlanet,\\r\\n name\\r\\n\\r\\n }\\r\\n }\",\"Variables\":null}";
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, new Uri("http://localhost:5000/"));
message.Content = new StringContent(jsonContent);
HttpResponseMessage result = await _Client.SendAsync(message);
string content = await result.Content.ReadAsStringAsync();
Console.WriteLine(content);
Console.ReadKey();
}
}
}
ApiHost.cs
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace ApiTest
{
public class ApiHost
{
public HttpListener Listener = new HttpListener();
private bool _Stop;
public void Start()
{
Listener.Prefixes.Add("http://+:5000/");
Listener.Start();
Task.Run(() =>
{
Semaphore semaphore = new Semaphore(4, 4);
while (!_Stop)
{
semaphore.WaitOne();
Listener.GetContextAsync().ContinueWith(async (contextTask) =>
{
try
{
semaphore.Release();
HttpListenerContext context = await contextTask.ConfigureAwait(false);
Process(context);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
});
}
});
}
private void Process(HttpListenerContext ctx)
{
try
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string text;
using (StreamReader reader = new StreamReader(ctx.Request.InputStream, ctx.Request.ContentEncoding))
{
text = reader.ReadToEnd();
}
stopwatch.Stop();
using (Stream output = ctx.Response.OutputStream)
{
using (StreamWriter writer = new StreamWriter(output) { AutoFlush = true })
{
writer.Write(text);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
Question:
Why do I have 360ms when using the internal library. When I use Insomnia for example I don't have this delay!
Kaspersky was the problem. After stopping the application it is running without any problems!
I am using a C# WebAPI project which would call an external API based on its URL. However, when I am trying to retrieve the data back, it hangs/freezes.
The code where it stops is:
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
I don't understand why it is stopping though. Could it be interfering with the API request I am also making? When I run this code as part of a unit test, I would get a response back within seconds. I don't think it is the API service itself, I think it is my code. I have already tried various API URLS. None of them work.
My full code is:
public static async Task<string> CallWebAPi<T>(string url)
{
string returnValue;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "GET";
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
returnValue = await strReader.ReadToEndAsync();
return returnValue;
}
Any help would be appreciated.
Possible deadlock ConfigureAwait(false), here are a good explanation from Stephen on what cause deadlocks.
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null).ConfigureAwait(false);
As a workaround you can use the synchronous functions, create a Task and await this task:
var response = await Task.Run(() =>
{
return (HttpWebResponse)request.GetResponse();
});
This is how i do my Request. Each Part is in an own Function. Its create the Request and you can get the Response synchronous.
public HttpWebRequest CreateRequest(string Url, string Method, string ContentType, object Content, List<RequestHeader> headers)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
request.Method = Method;
if (!string.IsNullOrWhiteSpace(ContentType)) request.ContentType = ContentType;
else if(Content != null) request.ContentType = "application/json";
if (Content != null)
{
var postData = Newtonsoft.Json.JsonConvert.SerializeObject(Content);
var data = Encoding.ASCII.GetBytes(postData);
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
}
foreach(RequestHeader header in Headers)
{
request.Headers.Add(header.Type, header.Value);
} //class at the end.
return request;
}
public string GetResponse(HttpWebRequest request)
{
var retval = "";
try
{
var response = (HttpWebResponse)request.GetResponse();
retval = ReadResponse(response);
response.Close();
}
catch (Exception ex)
{
resolveException(ex.Message);
}
return retval;
}
public string ReadResponse(HttpWebResponse response)
{
var retval = "";
try
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var responseText = reader.ReadToEnd();
retval = responseText;
}
}
catch (Exception ex)
{
resolveException(ex.Message);
}
return retval;
}
public class RequestHeader
{
public HttpRequestHeader Type { get; set; }
public string Value { get; set; }
}
you dont need Task.Factory.FromAsync. HttpWebRequest already supports asynchronous operations.
You have defined a generic method CalWebApi<T> but have never used a generic type
if your operation is async, use this.
public async Task<T> CalWebApiAsync<T>(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "GET";
using (var response = await request.GetResponseAsync())
{
using (var responseStream = response.GetResponseStream())
{
using (var streamReader = new StreamReader(responseStream))
{
var stringResult = await streamReader.ReadToEndAsync();
T objectResult = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(stringResult);
return objectResult;
}
}
}
}
var result = await CallWebApiAsync<YourType>("exteranlapiurl");
if your operation is not async, use this..
public T CalWebApi<T>(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "GET";
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var streamReader = new StreamReader(responseStream))
{
var stringResult = streamReader.ReadToEnd();
T objectResult = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(stringResult);
return objectResult;
}
}
}
}
var result = CallWebApi<YourType>("exteranlapiurl");
In the Asp.net MVC controller (GET method) I am calling external web service - for geolocation of IP - returning json data for IP location. How can I make the call to be async, hence the stack can continue while waiting the response from the service. When the GEO IP request finished I want to be able to make update to the db. Here is the current sync code:
public ActionResult SelectFacility(int franchiseId, Guid? coachLoggingTimeStampId)
{
//...
string responseFromServer = Helpers.GetLocationByIPAddress(userIpAddress);
HomeModels.GeoLocationModel myojb = new HomeModels.GeoLocationModel();
if (!String.IsNullOrEmpty(responseFromServer))
{
JavaScriptSerializer js = new JavaScriptSerializer();
myojb = (HomeModels.GeoLocationModel)js.Deserialize(responseFromServer, typeof(HomeModels.GeoLocationModel));
}
//...
}
public static string GetLocationByIPAddress(string ipAddress)
{
Stream resStream = null;
string responseFromServer = "";
try
{
string url = GeoLocationPath.FreeGeoIP + ipAddress;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
resStream = response.GetResponseStream();
StreamReader reader = new StreamReader(resStream);
responseFromServer = reader.ReadToEnd();
return responseFromServer;
}
catch (Exception ex)
{
//TODO handle this
}
finally
{
if (null != resStream)
{
resStream.Flush();
resStream.Close();
}
}
return responseFromServer;
}
Any suggestion - Thread, AsyncTask ?
Thanks
Make your ASP.NET MVC controller asynchronous:
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
Then use HttpClient.GetStringAsync and await its result:
public async Task<ActionResult> SelectFacility(
int franchiseId, Guid? coachLoggingTimeStampId)
{
//...
string responseFromServer = await Helpers.GetLocationByIPAddressAsync(
userIpAddress);
//...
}
public static async Task<string> GetLocationByIPAddress(string ipAddress)
{
using (var httpClient = new HttpClient())
return await httpClient.GetStringAsync(
GeoLocationPath.FreeGeoIP + ipAddress);
}
I am trying to use HttpWebRequest and HttpWebResonse asynchronously in a WinForms application without blocking my UI thread.
I saw this similar SO Question which explains how to do it. However, I am not 100% sure the accepted answer is the correct way to do it. The accepted answer is using BeginGetResponse.
According to the MSDN 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 some time, typically several seconds.
Can someone please provide me with the correct way to do this. Here is my C# function that I'm trying to make work "properly" without blocking my UI thread:
private IDictionary<string, object> CreateWebRequest(string endPoint, string method, string data, bool addAuthHeader)
{
var req = (HttpWebRequest)WebRequest.Create(endPoint);
req.Method = method;
if (addAuthHeader)
{
req.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + _accessToken.AccessToken);
}
if (!string.IsNullOrEmpty(data))
{
var utfenc = new UTF8Encoding();
byte[] buffer = utfenc.GetBytes(data);
req.ContentLength = buffer.Length;
req.ContentType = "application/x-www-form-urlencoded";
using (Stream strm = req.GetRequestStream())
{
strm.Write(buffer, 0, buffer.Length);
strm.Close();
}
}
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
if (_accessToken == null)
{
throw;
}
var responseError = (HttpWebResponse)e.Response;
if (responseError.StatusCode == HttpStatusCode.Unauthorized)
{
var stackTrace = new StackTrace();
var q = stackTrace.GetFrame(1).GetMethod().Name;
RefreshAccessCode();
req = (HttpWebRequest)WebRequest.Create(endPoint);
req.Method = method;
if (addAuthHeader)
{
req.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + _accessToken.AccessToken);
}
response = (HttpWebResponse)req.GetResponse();
}
}
string jsonResponse = null;
if (response != null)
using (Stream stream = response.GetResponseStream())
{
if (stream != null)
{
using (var reader = new StreamReader(stream))
{
jsonResponse = reader.ReadToEnd();
}
}
}
return DeserializeResponse(jsonResponse);
}
If you're open to something besides WebRequest, I'm a fan of System.Net.WebClient. It uses System.Threading.Tasks instead of BeginGetResponse() and EndGetResonse().
public async Task<Dictionary<string, object>> CreateWebRequest(string endPoint, string method, string data, bool addAuthHeader)
{
WebClient client = new WebClient();
if (addAuthHeader)
{
client.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + _accessToken.AccessToken);
}
byte[] buffer = null;
if (!string.IsNullOrEmpty(data))
{
var utfenc = new UTF8Encoding();
buffer = utfenc.GetBytes(data);
}
else
{
buffer = new byte[0];
}
return await client.UploadDataTaskAsync(endPoint, method, buffer)
.ContinueWith((bytes) =>
{
string jsonResponse = null;
using (var reader = new StreamReader(new MemoryStream(bytes)))
{
jsonResponse = reader.ReadToEnd();
}
return DeserializeResponse(jsonResponse);
});
}
}
To use your existing code, just add a method that is intended to execute on the UI thread, and create your web request on a separate thread. When the web request is complete you call the final method on the UI thread. So, a simple version would be:
//do some stuff that wont block the ui thread here
ThreadPool.QueueUserWorkItem((o) =>
{
IDictionary<string, object> result =
CreateWebRequest("lalala", "lol", "butts", true);
BeginInvoke(OnAsyncWebRequestComplete, result);
}, null);
private void OnAsyncWebRequestComplete(IDictionary<string, object> result)
{
//do some stuff on the UI thread here
}
private void RespCallback(IAsyncResult asynchronousResult)
{
try
{
WebRequest myWebRequest1 = (WebRequest)asynchronousResult.AsyncState;
// End the Asynchronous response.
WebResponse webResponse = myWebRequest1.EndGetResponse(asynchronousResult);
}
catch (Exception)
{
// TODO:Log the error
}
}
Now having the webResponse object, what is the easiest way to read its contents?
I would simply use the async methods on WebClient - much easier to work with:
WebClient client = new WebClient();
client.DownloadStringCompleted += (sender,args) => {
if(!args.Cancelled && args.Error == null) {
string result = args.Result; // do something fun...
}
};
client.DownloadStringAsync(new Uri("http://foo.com/bar"));
But to answer the question; assuming it is text, something like (noting you may need to specify the encoding):
using (var reader = new StreamReader(response.GetResponseStream()))
{
string result = reader.ReadToEnd(); // do something fun...
}
Here is one way to do it if the response is coming in from XML.
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create("https://www.yoururl.com");
WebResponse response = myReq.GetResponse();
Stream responseStream = response.GetResponseStream();
XmlTextReader reader = new XmlTextReader(responseStream);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Text)
{
Console.WriteLine("{0}", reader.Value.Trim());
}
Console.ReadLine();
}
internal string Get(string uri)
{
using (WebResponse wr = WebRequest.Create(uri).GetResponse())
{
using (StreamReader sr = new StreamReader(wr.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
}