I am trying to make asynchronous call to some server so that the main thread does not stop for the request.
I created my request something like this :
//Create Request
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Method = method;
request.ContentType = "application/XML";
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
byte[] bytes = encoding.GetBytes(data);
request.ContentLength = bytes.Length;
try
{
using (Stream requestStream = request.GetRequestStream())
{
// Send the data.
requestStream.Write(bytes, 0, bytes.Length);
}
request.BeginGetResponse((x) =>
{
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(x))
{
if (callback != null)
{
callback(response.GetResponseStream());
}
}
}, null);
}
catch (Exception ex)
{
//TODO: Log error
}
But still the thread stops for some time in this request. Especially on the line using
(Stream requestStream = request.GetRequestStream())
Is there any way by which we can make it completely asynchronous or some other better way so that our main thread does not hangs for this request.
using (HttpClient client = new HttpClient())
{
var content = new StringContent(data, Encoding.UTF8, "application/xml");
var response = await client.PostAsync(uri, content);
var respString = await response.Content.ReadAsStringAsync();
}
Need to use the async methods and await them.
using (var stream = await request.GetRequestStreamAsync())
{
await stream.WriteAsync(bytes, 0, bytes.Length);
}
Here is a simple sample which I am using to make async request. Mark your method with async keyword
using (var httpClient = new HttpClient())
{
string requestContent = string.Empty; // JSON Content comes here
HttpContent httpContent = new StringContent(requestContent, Encoding.UTF8, "application/json");
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(Verb, url);
httpRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpRequestMessage.Content = request.Verb == HttpMethod.Get ? null : httpContent;
response = httpClient.SendAsync(httpRequestMessage);
var responseContent = await response.Result.Content.ReadAsStringAsync();
}
Related
I am trying to access Web API in C# Code. I have tried below code in .Net Framework 4.0 but its shows an error message as “task httpresponsemessage does not contain a definition for getawaiter”.Please check below code and advise how to resolve this issue.
Code:-
static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
}
// HTTP POST
var gizmo = new Product() { Name = "Gizmo", Price = 100, Category = "Widget" };
response = await client.PostAsJsonAsync("api/products", gizmo);
if (response.IsSuccessStatusCode)
{
Uri gizmoUrl = response.Headers.Location;
// HTTP PUT
gizmo.Price = 80; // Update price
response = await client.PutAsJsonAsync(gizmoUrl, gizmo);
// HTTP DELETE
response = await client.DeleteAsync(gizmoUrl);
}
}
}
ok noted but what is the alternate solution to do in .net framework 4.0, if possible provide me example code
We can make use of the HttpWebRequest class available in System.Net namespace.
Along with that Newtonsoft.Json library (available as a nuget package) is also required for serialization and deserialization of data.
Inline is a rewrite of the requirement with HttpWebRequest
static void Run()
{
// HTTP GET
var getRequest = WebRequest.Create("http://localhost:9000/api/products/1") as HttpWebRequest;
getRequest.ContentType = "application/json;charset=UTF-8";
getRequest.Method = "GET";
HttpWebResponse getResponse = getRequest.GetResponse() as HttpWebResponse;
if (getResponse.StatusCode == HttpStatusCode.OK)
{
Stream responseStream = getResponse.GetResponseStream();
using (StreamReader sr = new StreamReader(responseStream))
{
var product = JsonConvert.DeserializeObject<Product>(sr.ReadToEnd());
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
}
}
else
{
throw new Exception(getResponse.StatusDescription);
}
// HTTP POST
var gizmo = new Product() { Name = "Gizmo", Price = 100, Category = "Widget" };
var postRequest = WebRequest.Create("http://localhost:9000/api/products") as HttpWebRequest;
postRequest.ContentType = "application/json;charset=UTF-8";
postRequest.Method = "POST";
Stream stream = postRequest.GetRequestStream();
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(gizmo));
stream.Write(buffer, 0, buffer.Length);
HttpWebResponse postResponse = postRequest.GetResponse() as HttpWebResponse;
if (postResponse.StatusCode == HttpStatusCode.OK)
{
//HTTP PUT
string gizmoUrl = postResponse.Headers["Location"];
var putRequest = WebRequest.Create(gizmoUrl) as HttpWebRequest;
putRequest.ContentType = "application/json;charset=UTF-8";
putRequest.Method = "PUT";
gizmo.Price = 80; //Update price
Stream putRequestStream = putRequest.GetRequestStream();
byte[] putRequestBuffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(gizmo));
stream.Write(buffer, 0, buffer.Length);
HttpWebResponse putResponse = putRequest.GetResponse() as HttpWebResponse;
//HTTP DELETE
var deleteRequest = WebRequest.Create(gizmoUrl) as HttpWebRequest;
deleteRequest.ContentType = "application/json;charset=UTF-8";
deleteRequest.Method = "DELETE";
HttpWebResponse deleteResponse = deleteRequest.GetResponse() as HttpWebResponse;
}
else
{
throw new Exception(postResponse.StatusDescription);
}
}
Note: I have not tested the code from my end. There might be minor modification required if things do not work as intended. However the motive is to provide an insight into HttpWebRequest class which can full fill your requirement in .Net 4
How would I convert this to HttpClient? What I'm looking to do is submit a Tweet to the Twitter api and get the response as Json. The HttpWebRequest is working fine but I just want to port it to HttpClient. I made an attempt at it in the second code example, but it's not actually sending or receiving the response.
HttpWebRequest request = null;
WebResponse response = null;
string responseCode = String.Empty;
try
{
string postBody = "status=" + EncodingUtils.UrlEncode(status);
request = (HttpWebRequest)HttpWebRequest.Create(resource_url);
request.ServicePoint.Expect100Continue = true;
request.UseDefaultCredentials = true;
request.PreAuthenticate = true;
request.Credentials = CredentialCache.DefaultCredentials;
request.ServicePoint.ConnectionLimit = 1;
request.Headers.Add("Authorization", authHeader);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (Stream stream = request.GetRequestStream())
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(postBody);
}
}
using (response = request.GetResponse())
{
response.ContentType = "application/json";
responseCode = ((HttpWebResponse)response).StatusCode.ToString();
}
}
catch (WebException ex)
{
if (ex.Status != WebExceptionStatus.NameResolutionFailure)
{
request.Abort();
request = null;
}
throw ex;
}
return responseCode;
This is what I've tried to get it work:
private async Task<string> MakeWebRequest1(string status, string resource_url, string authHeader)
{
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.Credentials = CredentialCache.DefaultCredentials;
clientHandler.PreAuthenticate = true;
clientHandler.AllowAutoRedirect = true;
string responseCode = "";
string postBody = "status=" + EncodingUtils.UrlEncode(status);
var request = new HttpRequestMessage()
{
RequestUri = new Uri(resource_url),
Method = HttpMethod.Post,
};
request.Headers.Add("Authorization", authHeader);
// request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
request.Content = new StringContent(postBody, Encoding.UTF8,"application/x-www-form-urlencoded");//CONTENT-TYPE header
using (HttpClient client = new HttpClient(clientHandler))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
// Stream stuff = await client.GetStreamAsync(resource_url);
using (HttpResponseMessage response = await client.SendAsync(request))
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
if(response.StatusCode == HttpStatusCode.OK)
{
responseCode = "OK";
}
}
}
clientHandler.Dispose();
return responseCode;
}
enter code here
I've tried to add another parameter to the request and it's always coming back as 401 unauthorized. I'm trying to create a Twitter thread. If I remove the in_reply_to_status_id then it's fine.
data = new Dictionary<string, string> {
["status"] = "#username + status,
["in_reply_to_status_id"] = "1167588690929115136"
};
The Twitter API describes it here https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update
Reference You're using HttpClient wrong to understand why a static client is being used.
static Lazy<HttpClient> client = new Lazy<HttpClient>(() => {
HttpClientHandler clientHandler = new HttpClientHandler {
Credentials = CredentialCache.DefaultCredentials,
PreAuthenticate = true,
AllowAutoRedirect = true
};
return new HttpClient(clientHandler);
});
private async Task<string> PostStatusRequestAsync(string status, string resource_url, string authHeader) {
using (var request = new HttpRequestMessage(HttpMethod.Post, resource_url)) {
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var data = new Dictionary<string, string> {
["status"] = status
};
request.Content = new FormUrlEncodedContent(data);
using (HttpResponseMessage response = await client.Value.SendAsync(request)) {
return response.StatusCode.ToString();
}
}
}
Note the use of the FormUrlEncodedContent for the request body, which will encode and concatenate the data as well as take care of the mime type header
...but it's not actually sending or receiving the response.
Ensure that the above is not invoked as a synchronous blocking call, like .Result, which could cause a deadlock.
For example, an async event handler can be used to make the async call
public async void onButtonClick(object sender, EventArgs args) {
//Non-blocking call
var tweetRequestCode = await PostStatusRequestAsync(TweetText, AuthUtils.GetResourceUrl(), AuthUtils.GetWebRequestHeader()));
//back on UI thread
//...
}
Reference Async/Await - Best Practices in Asynchronous Programming
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");
I am using third party API to add log API Call, i want to call this API Asynchronously to does not affect timing for main API call, also this process is low priority (even API result will not used.)
I have tried below code but it seems not work to me:
string strLogURL = "www.example.com";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(strLogURL);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(objAPILog);
streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
}
httpWebRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), httpWebRequest);
Can anyone have idea how to call API from WCF asynchronously?
you can create a method like this:
private static T Call<T>(string url, string body, int timeOut = 60)
{
var contentBytes = Encoding.UTF8.GetBytes(body);
var request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = timeOut * 1000;
request.ContentLength = contentBytes.Length;
request.Method = "DELETE";
request.ContentType = #"application/json";
using (var requestWritter = request.GetRequestStream())
requestWritter.Write(contentBytes, 0, (int)request.ContentLength);
var responseString = string.Empty;
var webResponse = (HttpWebResponse)request.GetResponse();
var responseStream = webResponse.GetResponseStream();
using (var reader = new StreamReader(responseStream))
{
reader.BaseStream.ReadTimeout = timeOut * 1000;
responseString = reader.ReadToEnd();
}
return JsonConvert.DeserializeObject<T>(responseString);
}
then you can call it asynchronously like this:
Task.Run(() => Call<dynamic>("www.example.com", "body"));
If asynchrony is all that you're looking for then you can achieve just that by using HttpClient with async/await. Then you can either fire & forget, or ensure that it has finished before leaving your calling-method.
public void DoSomeWork()
{
PerformWebWork("http://example.com", new object());
// Perform other work
// Forget webWork Task
// Finish
}
public async Task DoSomeWorkAsync()
{
Task webWorkTask = PerformWebWork("http://example.com", new object());
// Perform other work
// Ensure webWorkTask finished
await webWorkTask;
// Finish
}
public async Task PerformWebWork(string url, object objAPILog)
{
string serializedContent = new JavaScriptSerializer().Serialize(objAPILog);
using (HttpClient client = new HttpClient())
{
StringContent content = new StringContent(serializedContent);
HttpResponseMessage postResponse = await client.PostAsync(url, content);
}
}
http://ssw.com/profile/?apikey = skdwkdkfkkdj
I tried to use
public async Task<string> GetFromUriAsync(string requestUri, string token)
{
var client = new HttpClient();
client.BaseAddress = new Uri(BaseUri);
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("apikey", "=" + token);
HttpResponseMessage response = await client.GetAsync(requestUri);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
Then it returns null
Am I missing something or is it just totally wrong?
Thanks
You're trying to pass the API key in the header information of your HTTP request. What you need to do is just pass that whole URL without any additional header information.
IE: use "http://ssw.com/profile?apikey=abcdef" as the requestUri and send token as null. Also, remove the setting of the client.DefaultRequestHeaders.Authorization property. Authorization was meant to be a user/pass system and not a token-based system.
To test this, download Fiddler 4 (https://www.telerik.com/download/fiddler). Once you have fiddler installed, on the "Composer" tab, you can test different queries you need by putting the URL directly into the URL box and clicking "Execute". You'll then be able to use the inspectors to see the responses and figure out where you need to go from there.
Here are the classes I use for HTTP GET and POST operations:
public static string HTTPGET(string url)
{
try
{
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Timeout = 100000;
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
using (StreamReader resStream = new StreamReader(responseStream))
return resStream.ReadToEnd();
return null;
}
catch (Exception e)
{
Console.WriteLine(url);
Console.WriteLine(e);
return null;
}
}
public static string HTTPPOST(string url, string postData)
{
try
{
HttpWebRequest webRequest = (HttpWebRequest) WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "x-www-form-urlencoded";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
using (Stream requestStream = webRequest.GetRequestStream())
requestStream.Write(byteArray, 0, byteArray.Length);
using (Stream responseStream = webRequest.GetResponse().GetResponseStream())
if (responseStream != null)
using (StreamReader responseReader = new StreamReader(responseStream))
return responseReader.ReadToEnd();
return null;
}
catch (Exception e)
{
Console.WriteLine(url);
Console.WriteLine(postData);
Console.WriteLine(e);
return null;
}
}