I am using the Delegating Handler of Web Api 2.0 to intercept all my Web Api calls and I need to act before the action is executed.
I implemented the code as explained on Microsoft Docs as following:
public class MyHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
// do something
return base.SendAsync(request, cancellationToken);
}
}
And register the handler:
config.MessageHandlers.Add(new MyHandler());
But this code is executed after the controller method, which is not what I need. I want to execute the handler before, like I was doing on the pre-execute method of the old Action Filters for MVC.
Note
I am not using the Action Filters because on Microsfot Docs they said to stop using the Action Filters for Web Api 2.0 because they will be deprecated. So, what's the alternative when working with Web Api?
Your Problem is most likely concurrent execution in different Tasks. That is because you do not await the base.SendAsync() Call.
Something along these lines should probably solve your problem:
var response = await base.SendAsync(request, cancellationToken);
return response;
The execution of the MessageHandler you've implemented is executed through the Web API pipeline to which you registered it through config.MessageHandlers.Add(new MyHandler()); and should be independent from any action filters
If you are looking for addtional information look here.
Related
This is my first question but please advise if I can make improvements to the question.
I have a .NET Standard Web Api that uses Serilog to log requests to a Seq server. I want to add a correlation id to responses to use on the front end to track requests.
Here is the logging configuration Global.asax:
Log.Logger = new LoggerConfiguration()
.Enrich.With<HttpRequestIdEnricher>()
.Enrich.FromLogContext()
.WriteTo.Seq(ConfigurationManager.AppSettings["Seq:Url"])
.CreateLogger();
I have a message handler which adds a correlation id to all requests:
public class AddCorrelationIdToResponseHandler : DelegatingHandler
{
private const string CorrelationIdHeaderName = "X-Correlation-Id";
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var responseMessage = await base.SendAsync(request, cancellationToken);
var correlationId = request.GetCorrelationId().ToString();
responseMessage
.Headers
.Add(CorrelationIdHeaderName, correlationId);
return responseMessage;
}
}
The problem is the enricher (understandably) doesn't add the HttpRequestId to the response headers so I cannot use it in the front end and I cannot get the correlation id here into the Seq logs.
Here's what I have tried adding to the handler:
LogContext.PushProperty(CorrelationIdHeaderName, correlationId)
Log.ForContext(CorrelationIdHeaderName, correlationId)
both to no avail.
I have also wrapped the entire handler in a using statement with the LogContext statement.
Seq entry example here
I think the issue might be that the log context isn't valid here since the actual logging happens automatically in a different place.
Is there a way I can get access to the HttpRequestId to add it to the header or otherwise how can I can the correlation id to display as a property in the Seq logs? As a last resort, can one switch off the automatic Web Api logging and log the requests manually using Log.Information or Log.Error (in which case the log context should work in theory)?
Any assistance will be greatly appreciated.
This seems to be a fairly common issue, but none of the SO articles I have looked at have solved this for me.
I am working on a ASP.NET WebForms/MVC application running on IIS on Windows 10 (so not IIS Express) which is using jQuery AJAX to invoke a WebAPI application on a separate server. To get around CORS issues, and to add additional processing to all API calls, we implemented a server-side proxy using MVC controllers, so each call would end up somewhere like this:
[HttpPost]
public ActionResult Timesheets_Submit(Timesheet data)
{
var processedData = ProcessTheRequestInSomeWay(data);
var client = new SdkClient();
var results = client.Timesheets.Post(processedData);
return Json(results);
}
And this all worked quite successfully.
However, we are getting rather fed up of having to implement new server-side proxy methods each time we add a new API endpoint, so we decided to create a transparent server-side proxy using WebAPI, and have that do the real work.
The transparent server-side proxy is implemented like this:
public class TransparentProxyDelegatingHandler : DelegatingHandler
{
private static readonly Uri BaseUri = new Uri("https://my.apiserver.com");
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-Forwarded-For", request.GetClientIpAddress());
request.RequestUri = new Uri(BaseUri, request.RequestUri.PathAndQuery.Replace("/Proxy", string.Empty));
ProcessRequestInSomeWay(request);
var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
return response;
}
}
So a request to POST /Proxy/Timesheets will get translated into a call to POST https://my.apiserver.com/Timesheets and the response returned pretty much as-is.
The problem that I am having is that calls which use the PUT and DELETE verbs are being rejected as 404 Not Found by my UI (not by the API, I can still invoke that directly using e.g. Fiddler/Postman); the original proxy used those verbs, so it's not like they haven't been configured, it's just when I'm calling the delegating handler. The handler never gets invoked, so there's something happening in the routing engine that is causing MVC PUT/DELETE requests to work, but WebAPI PUT/DELETE requests to fail.
It turns out I was not registering the TransparentProxyDelegatingHandler correctly; I was registering it like this in my WebApiConfig:
configuration.MessageHandlers.Add(new TransparentProxyDelegatingHandler());
but as it turns out (thanks to https://blog.kloud.com.au/2013/11/24/do-it-yourself-web-api-proxy/), what I really wanted was:
configuration.Routes.MapHttpRoute(name: "proxy", routeTemplate: "proxy/{*path}",
handler: HttpClientFactory.CreatePipeline(
innerHandler: new HttpClientHandler(),
handlers: new DelegatingHandler[]
{
new TransparentProxyDelegatingHandler(),
}),
defaults: new { path = RouteParameter.Optional },
constraints: null);
I'm guessing that what was going on was that because I didn't have any actual ApiController implementations wired up to WebApi, it wasn't resolving correctly somehow in the early stages of the pipeline.
A colleague has written an Azure Mobile Service API which includes the following controller method:
public class SegmentationController : ApiController
{
// [...]
// POST api/<controller>/id
public async Task<string> Post(string id)
{
// [...]
I am trying to call that from a Windows Universal app. The calls to GET methods work without issue but I am failing to call that POST method. Here is what I've tried:
response = await client.PostAsync("api/segmentation/", new StringContent(item.Id));
// 405 Method Not Allowed
response = await client.PostAsync("api/segmentation/" + item.Id, new StringContent(""));
// 500 Internal Server Error
response = await client.PostAsync("api/segmentation/", new StringContent("id=" + item.Id));
// 405 Method Not Allowed
response = await client.PostAsync("api/segmentation/", new StringContent("{\"id\":" + item.Id + "}"));
// 405 Method Not Allowed
(N.B. System.Collections.Specialized.NameValueCollection used in Marc's answer is not available on WinRT / Windows Universal.)
It is possible that my second call is correct and that the error is in the server side code; we are exploring that possibility.
What is the correct way to make a POST call to an ASP.Net RESTful API method which expects a parameter called "id" of type string?
Your parameter is the problem. You have two options:
Use a query parameter instead of body. e.g. api/segmentation?id=abc
Add [FromBody] Attribute to your parameter. e.g. public async Task<string> Post([FromBody]string id)
Now your parameter is read from body. by default only complex types are read from body.
For more details see Parameter Binding in ASP.NET Web API
It was a server error. Once we had added error reporting code we could see that the problem was the server failing to load a C++ DLL it relied on due to an x64 /x86 mismatch on Azure. The call style that now works is the second one I list in the question:
response = await client.PostAsync("api/segmentation/" + item.Id, new StringContent(""));
I use OWIN selfhost WebAPI at console application and it use HttpListener.
_owinApplication = WebApp.Start<Startup>(new StartOptions(baseUri));
How i can monitor current active connections in some interval to my application ?
Given the nature of HTTP, it's not really possible to monitor "active" connections, since that concept doesn't really exist in HTTP. A client sends a request to your server, and it either fails, or receives a response.
When you see sites report current active users, that number is usually a qualified guess, or perhaps they monitor the clients with the use of websockets, or some sort of ajax polling.
You can create your own DelegatingHandler, register it in the WebApi pipeline and monitor current connections using overridden SendAsync method:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// TODO: add this request to some static container (like ConcurrentDictionary)
// let the request to be processed
HttpResponseMessage response;
try
{
response = await base.SendAsync(request, cancellationToken);
}
finally
{
// TODO: remove the request from the static container registered above
}
// return the response
return response;
}
This way you can not only monitor current connection count, but also all request information, like URL, IP, etc.
I'm create custom middleware and it solve my problem
Part of my application needs to act as a Proxy Server for a third party RESTful web service. Is there a way to set up Web API routing so that all requests of the same type will go to the same method?
For example, if the client sends in either of these GET requests I want them to go into a single GET action method that then sends on the request to the downstream server.
api/Proxy/Customers/10045
api/Proxy/Customers/10045/orders
api/Proxy/Customers?lastname=smith
The single action method for GET would pick up any one of these three requests and send them on to the respective service (I know how to work with HttpClient to make that happen effectively):
http://otherwebservice.com/Customers/10045
http://otherwebservice.com/Customers/10045/orders
http://otherwebservice.com/Customers?lastname=smith
I don't want to have to tightly couple my web service to the third party web service and replicate their entire API as method calls inside mine.
One workaround that I have thought of is to simply encode the target URL in JavaScript on the client and pass this into the Web API which will then only see one parameter. It would work, but I'd prefer to use the routing capabilities in Web API if possible.
Here's how I got this to work. First, create a controller with a method for each verb you want to support:
public class ProxyController : ApiController
{
private Uri _baseUri = new Uri("http://otherwebservice.com");
public async Task<HttpResponseMessage> Get(string url)
{
}
public async Task<HttpResponseMessage> Post(string url)
{
}
public async Task<HttpResponseMessage> Put(string url)
{
}
public async Task<HttpResponseMessage> Delete(string url)
{
}
}
The methods are async because they're going to use an HttpClient. Map your route like this:
config.Routes.MapHttpRoute(
name: "Proxy",
routeTemplate: "api/Proxy/{*url}",
defaults: new { controller = "Proxy" });
Now back to the Get method in the controller. Create an HttpClient object, create a new HttpRequestMessage object with the appropriate Url, copy everything (or almost everything) from the original request message, then call SendAsync():
public async Task<HttpResponseMessage> Get(string url)
{
using (var httpClient = new HttpClient())
{
string absoluteUrl = _baseUri.ToString() + "/" + url + Request.RequestUri.Query;
var proxyRequest = new HttpRequestMessage(Request.Method, absoluteUrl);
foreach (var header in Request.Headers)
{
proxyRequest.Headers.Add(header.Key, header.Value);
}
return await httpClient.SendAsync(proxyRequest, HttpCompletionOption.ResponseContentRead);
}
}
The URL combining could be more sophisticated, but that's the basic idea.
For the Post and Put methods, you'll also need to copy the request body
Also please note a HttpCompletionOption.ResponseContentRead parameter passed in SendAsync call, because without it, ASP.NET will spend an exremeley long time reading the content if the content is large (in my case, it changed a 500KB 100ms request into a 60s request).