Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

set web api route handler when using route attributes

I have a custom route handler witch i would like to use on different controllers. Right now the only way i get my controllers to use this route handler is to set it like this

RouteTable.Routes.MapHttpRoute(
    name: "CustomRouteHandler",
    routeTemplate: "/custom/{controller}/{action}",
    defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
    ).RouteHandler = new CustomRouteHandler();

I would really like to use route attributes like this

[HttpGet]
[Route(Name = "GetCart")]
public Cart Get()
{
    return _repository.Get();
}

But when i'm using route attributes and can't seem to figure out how to make sure i use the custom route handler. Preferable i would only use route attributes so if i could use a attribute like "RouteHandler" and here point to my "CustomRouteHandler" that would be perfect.

Is there a attribute i can use like this or could i in some way point everything in a MapHttpRoute into "/Custom" and then use route attributes from here and make all controllers have the custom handler?

Another option could maybe to make my own attribute that foreces a controller or method to use my custom route handler?

I'm trying to make a really crystal clear way for the developer to see that this controller or method is using a custom routehandler and if new developers should add another controller they could just use a special route like "/custom" or use a attribute.

Any ideas are very welcome. Thanks.

like image 448
MacGyver Avatar asked Nov 05 '15 11:11

MacGyver


1 Answers

Unfortunately, this is not possible in pure AttributeRouting due to some limitations:

Beware! Due to integration issues with the Web API WebHost framework, the following features will not work:

performance enhancements when matching routes, custom route handlers, querystring parameter constraints, subdomain routing, localization applied to inbound/outbound urls, and lowercasing, appending prefixes, etc to generated routes. These features all have to wait for vNext of the Web API.

So you have only three options.

1) Make routing in WebApiConfig.cs in classic way:

config.Routes.MapHttpRoute(
    name: "GetData",
    routeTemplate: "api/yourawesomecontroller/data",
    defaults: new { controller = "YourAwesomeController", action = nameof(YourAwesomeController.GetData) },
    constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) },
    handler: new YourCustomMessageHandler() { InnerHandler = new HttpControllerDispatcher(config) }
);

2) Also you may register YourCustomMessageHandler for all requests and implement route filtering inside handler itself:

class YourCustomMessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Method == HttpMethod.Get &&
            request.RequestUri.PathAndQuery.StartsWith("/api/yourawesomecontroller/data"))

        // ... custom handling for request ...

        var response = await base.SendAsync(request, cancellationToken);

        // ... custom handling for response ...

        return response;
    }
}

3) The last option is a custom route attribute:

public class MyRouteAttribute : Attribute
{
    public string Url { get; private set; }

    public MyRouteAttribute (string url)
    {
        Url = url;
    }
}

which you need to decorate action methods with. But in this case you need to register routes like in option 1) in your custom bootstrapper like this:

foreach (var controllerType in allControllerTypes)
{
    var attributes = System.ComponentModel.TypeDescriptor.GetAttributes(controllerType);
    var urlAttribute = (MyRouteAttribute) attributes[typeof(MyRouteAttribute)];
    var controllerName = controllerType.Name.Replace("Controller", "");

    config.Routes.MapHttpRoute(
        name: controllerName,
        routeTemplate: urlAttribute.Url,
        handler: new YourCustomMessageHandler() { InnerHandler = new HttpControllerDispatcher(config) }
    );
}

Note that you have to explicitly specify InnerHandler of YourCustomMessageHandler in order to pass requests down to pipeline to other handlers and controllers. See Microsoft explanation for details.

like image 56
RollerKostr Avatar answered Oct 13 '22 07:10

RollerKostr