Search code examples
c#asp.netasp.net-web-apiasp.net-web-api-routing

ASP.NET Web Api configuring a specific route not executing message handler


I am trying to configure a specific route to execute a message handler before it hits the controller, however the message handler is never executed.

I have the following controller:

public class TestController : ApiController
{
    [Route("private/users")]
    public IHttpActionResult Get()
    {
        return Ok();
    }
}

And the following route configuration:

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "private/users",
            defaults: new { id = RouteParameter.Optional },
            constraints: null,
            handler: new TokenValidationHandler() { InnerHandler = new HttpControllerDispatcher(config) }
        );

And The MessageHandler looks like this:

public class TokenValidationHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        //some logic to validate token
        return base.SendAsync(request, cancellationToken);
    }
}

The TokenValidationHandler is never executed which seems to be because of the routeTemplate when configuring the route. Is it possible to achieve route specific configuration with a hardcoded routeTemplate like "private/users" instead of the default "{controller}/{id}" format?

What I want to do is have my TokenValidationHandler fire for all of my private api calls and have public api calls bypass the TokenValidationHandler.


Solution

  • Attribute routes a matched before convention-based routes. The action in question is tagged with an Route attribute. If attribute routing is enabled then it means that it will not reach the convention-based route and thus not invoke the handler.

    Remove the route attribute

    public class TestController : ApiController { 
        public IHttpActionResult Get() {
            return Ok();
        }
    }
    

    The order of route registration also plays a factor for convention-based routes so make sure that general routes are registered after more specific routes

    config.Routes.MapHttpRoute(
        name: "PrivateApi",
        routeTemplate: "private/users",
        defaults: new { id = RouteParameter.Optional },
        constraints: null,
        handler: new TokenValidationHandler() { InnerHandler = new HttpControllerDispatcher(config) }
    );
    
    //more general route
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional },
        //...
    );
    

    Otherwise if attribute routing is your preference you can consider using an action filter.

    Based on stated intentions you would be better off using Authentication Filters in ASP.NET Web API 2