Very slow HttpClient SendAsync call - c#

After reading other answers I can't realize why SendAsync is so slow.
Calling same endpoint from Postman, I got a response in 160ms.
Calling from the code below, takes 10 seconds. I'm using a c# desktop application to make the call.
public static async Task<string> GetToken()
{
var url = "....";
var dict = new Dictionary<string, string>();
dict.Add("username", "foo");
dict.Add("password", "bar");
using (var client = new HttpClient(
new HttpClientHandler
{
Proxy = null,
UseProxy = false
}))
{
//bypass SSL
ServicePointManager.ServerCertificateValidationCallback = new
RemoteCertificateValidationCallback
(
delegate { return true; }
);
var req = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(dict) };
var res = await client.SendAsync(req); //10 seconds here!
if (res.StatusCode != HttpStatusCode.OK)
return string.Empty;
var token = await JsonConvert.DeserializeObject<TokenResponse>(res.Content.ReadAsStringAsync());
return token.access_token;
}
}

Your code is tangled and ignores IDisposable and this: HttpClient is intended to be instantiated once per application, rather than per-use.
Make reusable method for other-type requests
private static readonly HttpClient client = new HttpClient();
private async Task<T> PostDataAsync<T>(string url, Dictionary<string, string> formData)
{
using (HttpContent content = new FormUrlEncodedContent(formData))
using (HttpResponseMessage response = await client.PostAsync(url, content).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode(); // throws if 404, 500, etc.
string responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject<T>(responseText);
}
}
Usage
public static async Task<string> GetToken()
{
var url = "....";
var dict = new Dictionary<string, string>();
dict.Add("username", "foo");
dict.Add("password", "bar");
try
{
TokenResponse token = await PostDataAsync<TokenResponse>(url, dict);
return token.access_token;
}
catch (HttpRequestException ex)
{
// handle Exception here
return string.Empty;
}
}

Related

HttpRequst with parameters not workign

I am trying to make post request which works one way but other not.
Here is example how it works:
HttpClient client = new HttpClient();
string baseUrl = "https://192.168.56.1:45456/User/GetToken";
var response = await client.PostAsync(baseUrl + "?username=*******&password=****", null);
but then when I try adding it "normal way" at the endpoint i am getting nulls.
Here is how I try doing it:
HttpClient client = new HttpClient();
string baseUrl = "https://192.168.56.1:45456/User/GetToken";
Dictionary<string, string> parameters = new Dictionary<string, string>() { { "username", "ALEKS13" }, { "password", "asd" } };
FormUrlEncodedContent encodedParameters = new FormUrlEncodedContent(parameters);
var response = await client.PostAsync(baseUrl, encodedParameters);
What am I doing wrong in second code?
Here is receiving code:
[HttpPost]
[Route("/User/GetToken")]
public async Task<IActionResult> GetToken(string username, string password)
{
return await Task.Run<IActionResult>(() =>
{
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
return StatusCode(400);
Models.User user = Models.User.Get(username);
if (user.ValidatePassword(password))
return StatusCode(200, APIAuthorization.GenerateToken(username));
return StatusCode(403);
});
}

HttpClient with dynamic proxy

I have a method MultiLikeAsync(username, password, proxy) which can be executed multiple times and each time with different username, password, proxy or no proxy at all.
for (int i = 0; i < 15; ++i)
{
Class class = new Class();
await class.MultiLikeAsync(random_username, random_password, random_proxy);
}
This is my current setup:
public class Class
{
private static readonly HttpClient _httpClient;
static Class()
{
if (_httpClient == null)
{
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
_httpClient = new HttpClient(handler);
}
}
public async Task MultiLikeAsync(string username, string password, string proxy = null)
{
//_httpClient.Proxy = ??? // Can't do that
// first request
HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, "url");
// Polly
var timeoutPolicy = Policy.TimeoutAsync(1);
HttpResponseMessage httpResponse = await timeoutPolicy.ExecuteAsync(async ct => await _httpClient.SendAsync(httpRequest, ct), CancellationToken.None);
// second request
HttpRequestMessage httpRequest2 = new HttpRequestMessage(HttpMethod.Post, "different_url");
// Polly
HttpResponseMessage httpResponse2 = await timeoutPolicy.ExecuteAsync(async ct => await _httpClient.SendAsync(httpRequest2, ct), CancellationToken.None);
}
}
The problem with my solution is that I can't dynamically set _httpClient.Proxy for each MultiLikeAsync call. It has to be set along with HttpClientHandler during HttpClient instantiation or with multiple HttpClient instances.
It's a bad idea to reinstantiate HttpClient more than once, because it creates socket exhaustion and that's the reason they introduced IHttpClientFactory in .NET Core 2.1. Even if I used IHttpClientFactory, I would have had same problem, because it doesn't allow me to dynamically change proxies for each request. More info about it.
I need a solution immune to socket exhaustion (just like IHttpClientFactory) that allows me use a different proxy for each MultiLikeAsync call. Additionally, I want to keep my Polly Timeout.
Edit:
After your example given below, it's kinda the same. It doesn't close the TIME_WAIT requests. They are disposed after 240 seconds.
public class Class
{
private const int Timeout = 6;
public async Task MultiLikeAsync(string username, string password, string proxy = null)
{
using HttpClientHandler httpHandler = new HttpClientHandler();
if (proxy != null)
{
httpHandler.Proxy = new WebProxy(proxy, true);
}
if (httpHandler.SupportsAutomaticDecompression)
{
httpHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
using HttpClient httpClient = new HttpClient(httpHandler);
try
{
using HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, "url");
var timeoutPolicy = Policy.TimeoutAsync(Timeout);
HttpResponseMessage httpResponse = await timeoutPolicy.ExecuteAsync(async ct => await httpClient.SendAsync(httpRequest, ct), CancellationToken.None);
if (httpResponse.IsSuccessStatusCode)
{
string content = await httpResponse.Content.ReadAsStringAsync();
...
}
using HttpRequestMessage httpRequest2 = new HttpRequestMessage(HttpMethod.Post, "different_url");
HttpResponseMessage httpResponse2 = await timeoutPolicy.ExecuteAsync(async ct => await httpClient.SendAsync(httpRequest2, ct), CancellationToken.None);
if (httpResponse2.IsSuccessStatusCode)
{
string content = await httpResponse2.Content.ReadAsStringAsync();
...
}
}
catch (TimeoutRejectedException ex)
{
if (ex.InnerException is TaskCanceledException)
{
}
}
catch (HttpRequestException ex)
{
if (ex.InnerException is SocketException)
{
}
}
catch (Exception)
{
}
}
}

Xamarin POST method not returning correct value

I am having a problem with sending a POST request and getting a response. I have made local PHP script which returns some string values, and I can't get it to work with Xamarin.
This is the method I am using for to send the request:
public async Task<string> Post_Request()
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri("http://localhost/server.php");
request.Method = HttpMethod.Post;
request.Headers.Add("Accept", "application/json");
var client = new HttpClient();
HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(continueOnCapturedContext: false);
if (response.StatusCode == HttpStatusCode.OK)
{
return "OK";
}
else
{
return "BAD!";
}
}
When debbuging, the program does not go into the if or else code branches on the "if (response.StatusCode == HttpStatusCode.OK)" condition.
This is my PHP script:
<?php
return
"
{
"user":"01",
"name":"ime"
}
"
//echo "OK";
?>
This is a sample post request that I have used.
var objRequest = new CustomerDetailsRequest() {
customerId = 1
};
string url = $"/api/v1/CustomerDetails";
var requestBody = await Task.Run(() => JsonConvert.SerializeObject(objRequest));
using (var httpClient = new HttpClient())
{
CustomerDetailsResponse data = new CustomerDetailsResponse();
try
{
httpClient.BaseAddress = new Uri("http://localhost:3000");
var content = new StringContent(requestBody, Encoding.UTF8, "application/json");
var result = await httpClient.PostAsync(url, content);
var response = await result.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<CustomerDetailsResponse>(response);
if (result.IsSuccessStatusCode && result.StatusCode == HttpStatusCode.OK)
{
return data;
}
return null;
}
catch (Exception exp)
{
return null;
}
}
Let me know if this is confusing

How to PATCH data using System.Net.Http

I have uploaded a file to SharePoint and found out what id it has. Now I need to update some of the other columns on that listitem. The problem is that System.Net.Http.HttpMethod.Patch doesn't exist.
public static async Task<string> UpdateFileData()
{
var (authResult, message) = await Authentication.AquireTokenAsync();
string updateurl = MainPage.rooturl + "lists/edd49389-7edb-41db-80bd-c8493234eafa/items/" + fileID + "/";
var httpClient = new HttpClient();
HttpResponseMessage response;
try
{
var root = new
{
fields = new Dictionary<string, string>
{
{ "IBX", App.IBX }, //column to update
{ "Year", App.Year}, //column to update
{ "Month", App.Month} //column to update
}
};
var s = new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat };
var content = JsonConvert.SerializeObject(root, s);
var request = new HttpRequestMessage(HttpMethod.Put, updateurl);
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
response = await httpClient.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception ex)
{
return ex.ToString();
}
}
Modify the code as below.
public static async Task<string> UpdateFileData()
{
var (authResult, message) = await Authentication.AquireTokenAsync();
string updateurl = MainPage.rooturl + "lists/edd49389-7edb-41db-80bd-c8493234eafa/items/" + fileID + "/";
var httpClient = new HttpClient();
HttpResponseMessage response;
try
{
var root = new
{
fields = new Dictionary<string, string>
{
{ "IBX", App.IBX }, //column to update
{ "Year", App.Year}, //column to update
{ "Month", App.Month} //column to update
}
};
var s = new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat };
var content = JsonConvert.SerializeObject(root, s);
var request = new HttpRequestMessage(new HttpMethod("PATCH"), updateurl);
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
request.Content = new StringContent(content, System.Text.Encoding.UTF8, "application/json;odata=verbose");
response = await httpClient.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception ex)
{
return ex.ToString();
}
}
Or we can also use REST API to update list item by ID.
Refer to: SharePoint 2013 REST Services using C# and the HttpClient
It dependents whether .NET Core or .NET Framework is utilized, in case of `.NET Core HttpClient.PatchAsync Method could be utilized.
In case of .NET Framework ListItem could be updated like this:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.BaseAddress = new Uri("https://graph.microsoft.com");
var listItemPayload = new Dictionary<string, object>
{
{"Color", "Fuchsia"},
{"Quantity", 934}
};
var requestContent = new StringContent(JsonConvert.SerializeObject(listItemPayload));
requestContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
var response = await client.PatchAsync(new Uri($"https://graph.microsoft.com/v1.0/sites/{siteId}/lists/{listId}/items/{itemId}/fields"), requestContent);
var data = response.Content.ReadAsStringAsync().Result.ToString();
}
where PatchAsync is the extension method for HttpClient class:
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent iContent)
{
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri)
{
Content = iContent
};
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await client.SendAsync(request);
}
catch (TaskCanceledException e)
{
Debug.WriteLine("ERROR: " + e.ToString());
}
return response;
}
}
All the credits for extension method go to the author of this answer
Can't you just use the HttpMethod class constructor?
new HttpMethod("PATCH");
Source: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpmethod.-ctor?view=netframework-4.7.2#System_Net_Http_HttpMethod__ctor_System_String_

postAsync with header and content c#

I need to postAsync with header and content together. In order to get access to a website through Console Application in C#. I have my headers as an HttpHeader object with variable name header and my content named newContent as a string object with __Token, return, Email and Password. Now what I want to do is add newContent to header and then use postAsync(url, header+content) to make my POST request.
public async static void DownloadPage(string url)
{
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
using (HttpClient client = new HttpClient(handler))
{
using (HttpResponseMessage response = client.GetAsync(url).Result)
{
//statusCode
CheckStatusCode(response);
//header
HttpHeaders headers = response.Headers;
//content
HttpContent content = response.Content;
//getRequestVerificationToken&createCollection
string newcontent = CreateCollection(content);
using(HttpResponseMessage response2 = client.PostAsync(url,))
}
}
}
public static string GenerateQueryString(NameValueCollection collection)
{
var array = (from key in collection.AllKeys
from value in collection.GetValues(key)
select string.Format("{0}={1}", WebUtility.UrlEncode(key), WebUtility.UrlEncode(value))).ToArray();
return string.Join("&", array);
}
public static void CheckStatusCode(HttpResponseMessage response)
{
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
response.StatusCode,
response.ReasonPhrase));
else
Console.WriteLine("200");
}
public static string CreateCollection(HttpContent content)
{
var myContent = content.ReadAsStringAsync().Result;
HtmlNode.ElementsFlags.Remove("form");
string html = myContent;
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);
var input = doc.DocumentNode.SelectSingleNode("//*[#name='__Token']");
var token = input.Attributes["value"].Value;
//add all necessary component to collection
NameValueCollection collection = new NameValueCollection();
collection.Add("__Token", token);
collection.Add("return", "");
collection.Add("Email", "11111111#hotmail.com");
collection.Add("Password", "1234");
var newCollection = GenerateQueryString(collection);
return newCollection;
}
I did the very same thing yesterday. I created a seperate class for my Console App and put the HttpClient stuff in there.
In Main:
_httpCode = theClient.Post(_response, theClient.auth_bearer_token);
In the class:
public long Post_RedeemVoucher(Response _response, string token)
{
string client_URL_voucher_redeem = "https://myurl";
string body = "mypostBody";
Task<Response> content = Post(null, client_URL_voucher_redeem, token, body);
if (content.Exception == null)
{
return 200;
}
else
return -1;
}
Then the call itself:
async Task<Response> Post(string headers, string URL, string token, string body)
{
Response _response = new Response();
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, URL);
request.Content = new StringContent(body);
using (HttpResponseMessage response = await client.SendAsync(request))
{
if (!response.IsSuccessStatusCode)
{
_response.error = response.ReasonPhrase;
_response.statusCode = response.StatusCode;
return _response;
}
_response.statusCode = response.StatusCode;
_response.httpCode = (long)response.StatusCode;
using (HttpContent content = response.Content)
{
_response.JSON = await content.ReadAsStringAsync().ConfigureAwait(false);
return _response;
}
}
}
}
catch (Exception ex)
{
_response.ex = ex;
return _response;
}
}
I hope this points you in he right direction!
How about iterating over your Headers and adding them to the Content object:
var content = new StringContent(requestString, Encoding.UTF8);
// Iterate over current headers, as you can't set `Headers` property, only `.Add()` to the object.
foreach (var header in httpHeaders) {
content.Headers.Add(header.Key, header.Value.ToString());
}
response = client.PostAsync(Url, content).Result;
Now, they're sent in one method.
If you are still looking into this you can also add headers at the request level as well as the HttpClient level. This works for me:
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, URL);
request.Content = new StringContent(body);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

Categories