简体   繁体   中英

Using ASP.NET WebApi, how can I make a route exclude a set of controllers?

I have created two controllers for exporting type schemas and wadl and everything else is part of the api

So I created three routes, two for the special controllers (Schema and Wadl) and one for "everything else":

        config.Routes.MapHttpRoute(
            name: "Schemas",
            routeTemplate: "api/{controller}/{typeName}/{subType}",
            defaults: new { subtype = RouteParameter.Optional },
            constraints: new { controller = @"Schemas" }
        );

        config.Routes.MapHttpRoute(
            name: "Wadl",
            routeTemplate: "api/{controller}",
            defaults: null,
            constraints: new { controller = @"Wadl" }
        );

        config.Routes.MapHttpRoute(
            name: "api",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: somethingsomethingsomething }
        );

I tried following this page: http://stephenwalther.com/archive/2008/08/07/asp-net-mvc-tip-30-create-custom-route-constraints

And created a variation on their NotEquals class:

public class NotEqual : IRouteConstraint
{
    private IEnumerable<string> Matches;

    public NotEqual(IEnumerable<string> matches)
    {
        Matches = matches;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        bool output = true;
        var name = values[parameterName].ToString();
        foreach (var match in Matches)
        {
            if (name.Contains(match))
            {
                output = false;
                break;
            }
        }
        return output;
    }
}

And changed the 3rd route to be:

        config.Routes.MapHttpRoute(
            name: "api",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: new { controller = new NotEqual(new List<string> { "Schemas", "Wadl" }) }
        );

public class NotEqual : IRouteConstraint
{
    private IEnumerable<string> Matches;

    public NotEqual(IEnumerable<string> matches)
    {
        Matches = matches;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        bool output = true;
        var name = values[parameterName].ToString();
        foreach (var match in Matches)
        {
            if (name.Contains(match))
            {
                output = false;
                break;
            }
        }
        return output;
    }
}

Doesn't work, it still matches for all three controllers

It'll work fine with one route, but auto help page is ugly and misleading.

If I put a break on Match , it doesn't hit the break point.

The Regex constraints work perfectly, just the "everything else" one is not working

Why isn't that working?

Here's my own combination of other answers:

In Register() have the following:

        config.Routes.MapHttpRoute(
            name: "default",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: new { controller = @"^(?:(?!Wadl|Schemas).)*$" }
        );

        config.MapHttpAttributeRoutes();
        <snip>
        config.EnsureInitialized();

This gives you your default route for your controllers and you can exclude them by adding more | statements to the regex up there.

Then, to do the custom routes without having to worry about precedence, markup you Actions like this:

    [Route("schemas/{typeName}")]
    public XmlSchema GetSchema(string typeName) {}

    [Route("schemas/{typeName}/{subType}")]
    public XmlSchema GetSchema(string typeName, int subType)

Unmarked methods in those controllers will not have a route to them, just as if they were private.

    public string Test(int id)

the second route is more generic than the last as it registered before the one with id it will match it. just move the second one to the bottom and build your application it should work. as for constraints you are able to use something like new {id = @"\\d+" } but this only work if you have same format with different type of data in the url eg api/myController/123, api/myController/my Name. etc.

Honestly if you can, you should use AttributeRouting it is much clearer in code and much easier for someone new to a project to see what route is hitting what.

http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM