Web API response time - c#

I'm building a server monitoring system and want to send a request to a Web API and get the server's health in a JSON object if it's ok, if its database connections are working, its response time, etc.
How can I implement the response time, saying how long the Web API takes to answer the request?

If you want to implement Web Api Monitoring, you can create a custom DelegatingHandler to track action duration and status.
Here is a very basic example to measure operation duration. The duration is added to response (quite useless) ; it's better to store this kind of data to a dedicated repository.
public class MonitoringDelegate : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var watcher = Stopwatch.StartNew();
var response = await base.SendAsync(request, cancellationToken);
watcher.Stop();
//store duration somewheren here in the response header
response.Headers.Add("X-Duration", watcher.ElapsedMilliseconds.ToString());
return response;
}
}

You could start a Stopwatch on your client and stop it when you recive your awnser

Related

System.Net.HttpClient: SendAsync failed with OperationCanceledException without http request throught network

We use System.Net.Http.HttpClientfor calls between microservices inside k8s.
OS Linux(inside docker)
dotnet TargetFramework: netcoreapp2.1
server: Kestrel
protocol: http
Few days ago we noticed very strange behaviour of http calls:
some calls between microservice(near 2-3%) failed with error
System.OperationCanceledException: The operation was canceled.
at System.Net.Http.HttpClient.HandleFinishSendAsyncError(Exception e, CancellationTokenSource cts)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at OurCode.HttpServiceClient.GetStringResponseAsync(String methodUri, HttpMethod httpMethod)
...another our code...
after our timeout for http calls(it is 3 sec). But there was no logs about call inside callee service.
We enabled packetbeat for tracing http requests and also noticed, that no any http requests from caller service to callee service was executed.
CPU, memory and network for this services was OK all the time.
Simplified version of our code for http calls looks like:
public async Task<string> GetStringResponseAsync(String methodUri, HttpMethod httpMethod)
{
int timeoutInMilliseconds = 3000;
var tokenSource = new CancellationTokenSource();
var rm = new HttpRequestMessage(httpMethod, methodUri);
Task<HttpResponseMessage> httpTask = HttpClient.SendAsync(rm, tokenSource.Token);
tokenSource.CancelAfter(timeoutInMilliseconds);
HttpResponseMessage response = await httpTask;
await EnsureSuccessStatusCode(response);
return await response.Content.ReadAsStringAsync();
}
Any ideas about what problem can cause this strange behaviour without http request through network, and what can I do for further investigation?
It just meant that the web service did not respond.
HttpClient throws a TaskCanceledException, (which inherits from OperationCanceledException) when the timeout elapses. It is not intuitive and doesn't make any sense to me (and others), but that's what it does unfortunately.
There is some discussion about it here (a few people mentioned some workarounds to distinguish timeout from a true cancel, if you care).
I don't know if this will help anyone in the future but I had the same issues. Turns out the server I was hosting on was not automatically using the proxy address by default. I ended up having to add the following to my code:
using (var handler = new WinHttpHandler()){
handler.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseCustomProxy;
handler.Proxy = new WebProxy(server, port);
//the rest of your code.....
}
This manually added the proxy server information and my console app began working as expected.

Async progress in ASP .NET Core middleware?

I have a web-api with mvc that's doing a bunch of startup initialization which will take a few minutes. I want the url to respond to a request during this time with a progress-indicator. My thinking was to use a middleware to accomplish this something like this:
public async Task Invoke(HttpContext httpContext)
{
await httpContext.Response.WriteAsync("Loading...");
await Task.Delay(5000); // the initialization-stuff (which is not started here but just waited on)
httpContext.Response.Clear();
await _next(httpContext); // continue to my MVC-page
}
However this does not seem to work (ERR_INCOMPLETE_CHUNKED_ENCODING). How do I properly clear/reset the respons so that I can write a new real response once the initialization is done.
I resorted to something like this instead (good enough):
public async Task Invoke(HttpContext httpContext)
{
if (!task.IsCompleted)
await httpContext.Response.WriteAsync("Loading...");
else
await _next(httpContext); // continue...
}
Once you have sent data to the client you can't take it back. You can't replace an existing page, only append.
You can therefore do two things:
You delay the response until initialization is complete (likely not feasible).
You send something else and end the request. You could make the page that you are sending poll the server using AJAX to see if initialization has been completed. Then, the page can reload itself.
Create a new API endpoint that replies with the initialization status. Make page page poll that endpoint every few seconds.

Add CorrelationId to remote calls in ASP.NET MVC5

I use Delgating handler to add correlation ids to inward requests to my application.
But, my MVC app makes frequent calls to remote domain https://remoteservice.net/xyz via an webapi client provided by them. The remote service API allows users to set correlation id via request header, but the webapi client doesn't provide a way to expose that.
So I am now thinking to set correlation id via owin pipeline for all outgoing remote calls from my MVC app. Is this possible? Any help?
You could extend an HttpClientHandler and add a correlationId header there:
public class CorrelatingHandler : HttpClientHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("currelationId", Guid.NewGuid());
return base.SendAsync(request, cancellationToken);
}
}
using(var client = new HttpClient(new CorrelatingHandler())){
.....
}
source: https://forums.asp.net/post/6025521.aspx

The difference between OWIN and IIS on handling HTTP HEAD request when the api is not support the HEAD request

I have a 2 services(WebApi), one is azure cloud service that using IIS host and another one is azure service fabric stateless service that using Owin host. There is a handler that like below, and both services register this handler to handle the context(config.MessageHandlers.Add(new ContextHandler())).
Currently, what I met is: If I call cloud service api(just ping) using HTTP HEAD request, this will return '405 method not allowed'. But if I call fabric service, I will get 'Could not get any response' with 'there was an error connecting to...'.
When debug the code. For the cloud service using IIS, in the below code, the response.Content has no value. But for the fabric service using Owin, the response.Content has value '405 Method not allowed'.
My question is, what the difference between them? Why the Owin will return a response with content back as the request is HEAD request?
Thanks.
public class ContextHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// initialize context
using (new ApiRequestContextWrapper(request))
{
var context = ApiRequestContext.Current;
context.Log.Trace(
$"API Call {request.Method.Method}
{request.RequestUri.GetLeftPart(UriPartial.Path)}");
var response = await base.SendAsync(request,
cancellationToken).ConfigureAwait(false);
return response;
}
}
}

WebAPI - Log binding information at MessageHandler?

I need to log (not Trace) all requests and responses from webAPI (v1) and store the information in DB.
I thought that the most appropriate place to do it is via a MessageHandler.
So :
public class LogRequestAndResponseHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var MyRequest = request;
var MyResponse = task.Result;
var responseBody = task.Result.Content;
//log db....
return task.Result;
}, cancellationToken);
}
}
Please notice that I'm reading the request and response after the SendAsync ( in a continuation) because I want a single write to DB.
But:
I thought to myself : "it would be nice if I could extract the method binding information at the same place (in the MessageHandler)"
For example , consider this code :
[HttpGet]
[ActionName("GetGraph")]
public HttpResponseMessage BlaBla(string Member_ID, int DateDiff)
{
//...
}
And this request :
http://es.com/api/claims/GetGraph?Member_ID=123&DateDiff=5&NotExists=2
Question:
Is it possible(and how) to extract something like this :
User sent match-able Member_ID with value 123
User sent match-able DateDiff with value 5
User sent non-match-able NotExists with value 2
At this stage :
You are correct about using a message handler for logging the raw request and response but then were you able to log the request body using the code above? If you actually bind the body to an action method parameter, parameter binding would actually read and empty the request body stream. So, I don't think your idea of logging the request body in task continuation will work.
Logging data about binding in a message handler is complicated and depends a lot on your action methods. Message handler request processing runs before model binding and when the response processing part runs, the notion of models do not exist, since serialization is already done. BTW, take a look at my blog post for detecting extra fields in the request, when you are using JSON payload.
http://lbadri.wordpress.com/2014/01/28/detecting-extra-fields-in-asp-net-web-api-request/
You can possibly write an action filter to look at the extra fields from the model, as mentioned in that post and store that info in request dictionary (Request.Properties) and pick that up later in the message handler and write to database. It is not straight forward though.
Alternatively, look at the built-in tracer to see if it is helpful.

Categories