Performance improvement of of HttpResponseMessage client.SendAsync in c# - c#

I have code like following
public async Task<object> GetMethod()
{
object result = null;
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "www.fo");
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage);
if (ok)
{
dynamic data = JObject.Parse(responseMessage.Content.ReadAsStringAsync().Result);
result = data.result.paameter1.tables.paamete2.row.ToString();
}
return result
}
Observation is client.SendAsync(requestMessage); takes too much time , is there any other better way of doing it considering performance ?

this would be better
private async Task<T> Post<T>(string url, object obj) where T : class
{
var jsonString = _jsonHelper.SerializeObject(obj);
var stringContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(url, stringContent).ConfigureAwait(false);
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if(!response.IsSuccessStatusCode)
{
//throw
}
var responseObj = _jsonHelper.DeserializeObject<T>(responseString);
return responseObj;
}
usage
public Task<SomeTypedObject> DoSomething(int someValue)
{
var url = "someUrl";
var requestObject= new ClassForRequest { proptery1 = someValue};
return Post<SomeTypedObject>(url, requestObject);
}
the main issue with yours i not using await on ReadAsStringAsync, ur is thread blocking.
but i highly suspect this is not the reason for the "performance" you are wanting.
What is the call doing?
how much data is it returning?
how far is the endpoint?/ how powerful is the endpoint machine? / what its ping to that machine?
PS, show your HttpClient life-time, the problem is most likely there.
is performance for you how long it takes for 1 request or its ability to do this x times a minute.

Related

C# async - creating a task properly

I'm creating a Task in C# but I'm not sure what I do is correct. I'm using Restsharp and in Restsharp there are two methods: Execute and ExecuteAsync. I want to do an Async call but I also need to return data to the client without blocking the execution.
Therefore I created a task which will use Execute instead of ExecuteAsync. The reason why is because I have to wait until I get a response back and then return it in the right data structure. So I thought there is no use in using ExecuteAsync if I have to await it in a Task...
My code looks as follows:
public Task<Response> ExecuteAsync()
{
return new Task<Response>(() =>
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = client.Execute(request);
return new Response()
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
});
}
Is this correct? The client should be able to call ExecuteAsync without blocking the execution.
I strongly suspect you should really just use ExecuteAsync and write an async method:
public async Task<Response> ExecuteAsync()
{
var client = new RestClient(URL);
if (_useBasicAuth)
{
client.Authenticator = new HttpBasicAuthenticator(_username, _password);
}
var request = RequestBuilder(_method);
var response = await client.ExecuteAsync(request).ConfigureAwait(false);
return new Response
{
HttpStatusCode = response.StatusCode,
HttpStatusDescription = response.StatusDescription,
Content = response.Content,
Cookies = ExtractCookies(response.Cookies),
Headers = ExtractHeaders(response.Headers)
};
}

Why RestAPI occasionally returns 429 TooManyRequest response?

I send a request to the API and sometimes receive the response with an HTTP 429 status code (TooManyRequests).
On average, for 10 requests, 2 will return 429 response and the remaining 8 return the correct value.
It also happened to me that when it was the first request (so there is no option for TooManyRequests)
public static List<ResponseObject> GetProductsFromRestAPI(int[] ProductIdArray )
{
List<ResponseObject> products = new List<ResponseObject>();
string action;
for (int i = 0; i < ProductIdArray.Length; i++)
{
action = "products/" + ProductIdArray[i].ToString();
client = AddHeadersToClient(action, new RestClient("https://api.usedbythiscode.com/")); //required by this API.
var currentProduct = RequestsLib.GetProduct(client, action);
products.Add(currentProduct);
}
return products;
}
public static Product GetProduct(RestClient restClient, string action) //todo test this for bugs
{
var result = new Product();
var request = new RestRequest(action, Method.GET);
var response = SendRequest(restClient, request);//Here I sometimes get response with 429.
//.. Other code
return result;
}
public static async Task<IRestResponse> SendRequest(RestClient restClient, RestRequest request)
{
return await restClient.ExecuteGetAsync(request);
}
Temporarily resolved it by sending another request with do while loop and usually second request return right answer.
do
{
SendRequest(restClient, request);
}
while (StatusCode != 200);
Where could the cause of the error lie?
Is it possible that I have unclosed requests?
Is creating multiple RestSharp clients a good practice?
EDIT:
The problem was on the server side. All I had to do was report the bug to the admins who provided the API. Thank you for help.
429 is Too Many Requests. Most APIs have some kind of rate-limiting in place so that a single client can't take down their server with too many requests.
The proper response for 429 is to retry. I recommend using Polly for retry logic, but be aware that HandleTransientHttpError doesn't consider 429 a transient error.
I agree with #mason, you should use async method with Task<> and await response Here is the part of login side of my mobileApp-project in Xamarin. You may want to see how to use async with Task<> easily.
public async Task<BSUser> ValidateUser(string userName, string password)
{
string url = Xamarin.Essentials.Preferences.Get(Constants.URL_KEY, "") + "/api/Validateuser";
HttpClient _Client = new HttpClient();
var data = new Dictionary<string, string>
{
{"userName", userName},
{"password", password}
};
string jsonData = JsonConvert.SerializeObject(data);
HttpContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");
try
{
HttpResponseMessage httpResponse = await _Client.PostAsync(url, content);
if (httpResponse.IsSuccessStatusCode)
{
try {
var responseData = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = JsonConvert.DeserializeObject(responseData).ToString();
UserInfo userInfo = JsonConvert.DeserializeObject<UserInfo>(result);
BSUser value = new BSUser();
value.UserName = userInfo.userCode;
return value;
}
catch (Java.Net.SocketException e)
{
Console.WriteLine("Hata", e);
return null;
}
}
else
{
return null;
}
}
catch (SystemException)
{
return null;
}
}

Wpf Desktop Api call duration more longer than actual api response time about 8-10 seconds

Here is case:
I have wpf application on about 80 clients and they communicate single .net framework api for data process. I have stopwatch tracker some places for tracking durations on Wpf and api apps. Code samples :
My api attribute :
public class DurationControlLoggerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var controller = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var action = actionContext.ActionDescriptor.ActionName;
actionContext.ActionArguments.Add("_stopwatch_", Stopwatch.StartNew());
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (ConfigurationManager.AppSettings["ApiLogger"].ToLower().Trim() != "true")
return;
var controller = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var action = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
var stopWatch = (Stopwatch)actionExecutedContext.ActionContext.ActionArguments["_stopwatch_"];
stopWatch.Stop();
var scope = actionExecutedContext.ActionContext.Request.GetDependencyScope();
var commonService = (ICommonService)scope.GetService(typeof(ICommonService));
commonService.InsertLog(new Model.Common.LogModel
{
InsertDate = DateTime.Now.ToString(),
LogLevel = "API",
MachineName = "API",
Message = $"Controller : {controller} - Action : {action} - TotalSeconds : {stopWatch.Elapsed.TotalSeconds}",
StackTrace = string.Empty
});
}
}
Action sample :
[HttpPost]
[DurationControlLogger]
public bool InsertProduct(ProductModel model)
{
return _mainService.TracingService.InsertProduct(model);
}
This action process duration is about 0,03 seconds. On the other hand, wpf api call duration is about 10 seconds. Wpf code blocks are down below:
var Stopwatch = Stopwatch.StartNew();
var isSuccess = DataHelper.InsertProduct(Product);
Stopwatch.Stop();
if (Stopwatch.Elapsed.TotalSeconds > 2)
DataHelper.InsertTraceLog($"ProducrtBusiness - InsertProduct TotalSecond : {Stopwatch.Elapsed.TotalSeconds}");
DataHelper.InsertProduct method does basic http post request. Code is here :
public static class HttpClientHelper
{
public static T Post<T>(object model, string url)
{
var resultStatus = false;
T resultData = default(T);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(url, content).Result;
if (response.IsSuccessStatusCode)
{
string data = response.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<T>(data);
resultStatus = response.StatusCode == System.Net.HttpStatusCode.OK;
resultData = result;
}
}
return resultStatus ? resultData : default(T);
}
.....
Does anyone has any idea about that situation?
EDIT
I add one last log code. Here is code:
`
var stopwatch = Stopwatch.StartNew();
HttpResponseMessage response = client.PostAsync(url, content).Result;
stopwatch.Stop();
if (stopwatch.Elapsed.TotalSeconds > 2)
{
AppendToFile($"{DateTime.Now.ToString()} - {model.ToString()} - {stopwatch.Elapsed.TotalSeconds} - Content: {jsonData}");
}
`
This log duration still sometimes is 8-10 sec.
Well, it is likely (from what Ive seen) that you are running this code on the main message pump, (UI thread). During this method call you are performing an asynchronous call (PostAsync) and calling the 'Result' - making it run synchronously. However, while your UI thread was waiting, some other messages (UI updates?) where queued on the message pump, and were processed before your call to end the stopwatch (higher DispatcherPriority?) - delaying the timer end.
This is pure speculation, but I would recommend that you use some async/await in your asynchronous code, and try and perform these data access tasks on a separate thread
I changed my HttpClientHelper class post method to this :
public static async Task<T> PostAsync<T>(object model, string url)
{
var resultStatus = false;
T resultData = default(T);
using (var client = new HttpClient(
new HttpClientHandler()
{
UseProxy = false
}
))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var jsonData = JsonConvert.SerializeObject(model);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var stopwatch = Stopwatch.StartNew();
HttpResponseMessage response = await client.PostAsync(url, content);
stopwatch.Stop();
if (stopwatch.Elapsed.TotalSeconds > 2)
{
AppendToFile($"{DateTime.Now.ToString()} - {model.ToString()} - {stopwatch.Elapsed.TotalSeconds} - Content: {jsonData}");
}
if (response.IsSuccessStatusCode)
{
string data = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<T>(data);
resultStatus = response.StatusCode == System.Net.HttpStatusCode.OK;
resultData = result;
}
}
return resultStatus ? resultData : default(T);
}
client.PostAsync() method has to run async for my issue. I think that related some thread managment issues. But this code works for me now.

POST an empty body via HTTP Client

I am trying to send an empty body to a Post Request but it does not execute.
I have already tried this:
Post an empty body to REST API via HttpClient
static async Task<string> CancelSale(string mainUrl, string bearerInfo,string systemNumber)
{
var cancelsaleUrl = mainUrl + $"api/sale/cancel/{systemNumber}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerInfo);
var data = new StringContent(null, Encoding.UTF8, "application/json");
var saleResponse = await client.PostAsync(cancelsaleUrl, data);
var responseBody = await saleResponse.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
return responseBody;
}
But it just does not execute, no exception.
I also tried this :
var saleResponse = await client.PostAsync(cancelsaleUrl, null);
which also does the same result.
Any ideas?
The problem was very simple. I had the result of whole this method in a variable and It did not await the method:
var cancelSale = CancelSale(mainUrl, bearerInfo, systemNumber);
Once it reaches anything that awaits it stops and leaves the method.
Here is the working code:
var cancelSale = await CancelSale(mainUrl, bearerInfo, systemNumber);
static async Task<string> CancelSale(string mainUrl, string bearerInfo,string systemNumber)
{
var cancelsaleUrl = mainUrl + $"api/sale/cancel/{systemNumber}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerInfo);
var saleResponse = await client.PostAsync(cancelsaleUrl, null);
var responseBody = await saleResponse.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
return responseBody;
}

C# HttpClient async POST request Task not returning a value

I'm executing an async POST request using a HttpClient in C#/Xamarin:
private async Task<string> ServicePostRequest (string url, string parameters)
{
string result = String.Empty;
using (var client = new HttpClient()) {
HttpContent content = new StringContent (parameters);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue ("application/x-www-form-urlencoded");
client.Timeout = new TimeSpan (0, 0, 15);
using(var response = await client.PostAsync(url, content)){
using (var responseContent = response.Content) {
result = await responseContent.ReadAsStringAsync ();
Console.WriteLine (result);
return result;
}
}
}
}
When I execute the following code, the expected result (JSON) is being logged correctly in the terminal:
Task<string> result = ServicePostRequest("http://www.url.com", "parameters");
Now, I would like to get this result into a variable to be able to parse it. However, when I use the following code, no result is being logged at all and the application is frozen:
Task<string> result = ServicePostRequest("http://www.url.com", "parameters");
string myResult = result.Result;
Also when I use the result.Wait() method, the application doesn't respond at all.
Any help would be highly appreciated.
Since ServicePostRequest is an awaitable method, change this:
Task<string> result = ServicePostRequest("http://www.url.com", "parameters");
string myResult = result.Result;
To:
string result = await ServicePostRequest("http://www.url.com", "parameters");
Side Note: Make sure the calling method is an Asynchronous method.

Categories

Resources