I'm facing an issue with the execution order of the ASP.NET Web API request pipeline.
According to the ASP.NET Web API documentation (available here), global message handlers are supposed to be executed before the routing mechanism.
On this image, MessageHandler1
is a global message handler whereas MessageHandler2
is specific to Route 2.
I created a very simple example to show that there seems to be an issue in the execution order… or I'm really missing something important.
I have this controller
public class FooController : ApiController {
[HttpPut]
public string PutMe() {
return Request.Method.Method;
}
}
It only accepts PUT
requests.
The application is configured as such:
protected void Application_Start() {
var configuration = GlobalConfiguration.Configuration;
configuration.MessageHandlers.Add( new SimpleMethodOverrideHandler() );
configuration.Configuration.Routes.MapHttpRoute(
name: "Foo",
routeTemplate: "api/foo",
defaults: new { controller = "foo", action = "putme" },
constraints: new { put = new HttpPutOnlyConstraint() }
);
}
SimpleMethodOverrideHandler
is a very simple DelegatingHandler
that just changed the request's method according to a "method"
parameter in the query string.
public class SimpleMethodOverrideHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken ) {
var method = request.RequestUri.ParseQueryString()["method"];
if( !string.IsNullOrEmpty( method ) ) {
request.Method = new HttpMethod( method );
}
return base.SendAsync( request, cancellationToken );
}
}
So basically, requesting /api/foo?method=put
in my browser would fire up FooController
's PutMe
method.
Indeed, as seen earlier, the message handler treats the requests before it gets passed to the HttpRoutingDispatched
.
Finally, here's how the the constaint HttpPutOnlyConstraint
is defined:
public class HttpPutOnlyConstraint : IHttpRouteConstraint {
public bool Match( HttpRequestMessage request,
IHttpRoute route,
string parameterName,
IDictionary<string, object> values,
HttpRouteDirection routeDirection ) {
return request.Method == HttpMethod.Put;
}
}
Well the problem is that when I request /api/foo?method=put
within my browser, the program first enters HttpPutOnlyConstraint
's Match
method, which is wrong.
If we refer to the previously linked image, the message handler is supposed to be executed first, unfortunately it is not.
So, of course, Match
returns false
and no controller/action is found for the request, 404 happens.
If I remove the constraint from the route definition, the program enters SimpleMethodOverrideHandler
, the request's method gets changed successfully and it is able to match and execute my controller's method.
Am I doing something wrong? Is there a secret configuration parameter to know in order to do such things? :-)
If anyone needs the whole project, it's available here [7KB zip file].
Thank you.
You are confusing routing engine with Web API pipeline.
HttpRoutingDispatcher
is not a routing engine concept. The route constraints will be processed first because your underlying host needs to build a route table and match the route for your request.HttpRoutingDispatcher
is simply another implementation ofHttpMessageHandler
and all it does is it examines theIHttpRoute
of the route that has been matched, and chooses which message handler to call next. If there is no per-route handler present, it delegates the processing toHttpControllerDispatcher
.