根据参数名称添加全局路由约束

时间:2014-03-13 08:59:17

标签: asp.net-mvc asp.net-mvc-routing attributerouting asp.net-mvc-5.1

使用库AttributeRouting,我能够将属性路由配置为根据参数名称使用默认路由约束

例如,要为以结尾的所有路径参数添加 RegexRouteConstraint

routes.MapAttributeRoutes(cfg =>
    {
        cfg.AddDefaultRouteConstraint(@"year$",
            new RegexRouteConstraint(@"^([1-2]\d{3})$"));
    }
);

我知道custom route constraints in MVC5,但我真的希望能够根据参数名称按惯例添加一些约束。

是否可以对MVC5.1的属性路由功能执行相同的操作?

1 个答案:

答案 0 :(得分:1)

我有同样的需求,并找到了解决方案,虽然我没有彻底测试它。我的场景是一个多租户API,其中的路由都以“api / {tenant} / ...”开头。租户是从web.config中提取的,所以我的复杂性增加了我的自定义解析器应该是单例。下面的解决方案适用于Web API,但我希望它可以在MVC中使用一些命名空间调整。

创建IHttpRouteConstraint的实现。这是我的:

public class TenantRouteConstraint : IHttpRouteConstraint
{
    public const string TenantKey = "tenant";

    private readonly ISet<string> _tenants;

    public TenantRouteConstraint()
    {
        _tenants = new HashSet<string>();
        foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
        {
            _tenants.Add(connectionString.Name.ToLowerInvariant());
        }
    }

    private static string GetTenant(IDictionary<string, object> values)
    {
        object tenant;
        if (values.TryGetValue(TenantKey, out tenant))
        {
            return tenant.ToString().ToLowerInvariant();
        }
        return null;
    }

    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        var tenant = GetTenant(values);
        return tenant != null && _tenants.Contains(tenant);
    }
}

那部分很容易,然后就变得棘手了。这些路由通常在WebApiConfig.Register中初始化,该委托作为委托传递给Global.asax中的GlobalConfiguration.Configure。但是,基于属性的路由不会在Register方法末尾的Routes集合中填充。为了解决这个问题,我在WebApiConfig中添加了一个RegisterRouteConstraints方法,该方法在Register之后被调用。

RegisterRouteConstraints方法遍历Routes集合,如果路由模板中存在“{tenant}”,则添加我的约束。 Routes集合包含三种类型的路由:RouteCollectionRoute,HostedHttpRoute和LinkGeneratioRoute。基于属性的路由在RouteCollectionRoute中,但这些类是内部的,所以我无法直接测试该类型。幸运的是它实现了IEnumerable<IHttpRoute>所以我检查了它。

public static void RegisterRouteConstraints(HttpConfiguration config)
{
    var tenantConstraint = new TenantRouteConstraint();
    AddConstraint(config.Routes, "tenant", tenantConstraint);
}

private static void AddConstraint(IEnumerable<IHttpRoute> routes, string key, IHttpRouteConstraint constraint)
{
    foreach (var route in routes)
    {
        if (route.RouteTemplate.Contains("{" + key + "}") && !route.Constraints.ContainsKey(key))
        {
            route.Constraints.Add(key, constraint);
        }

        var routeCollection = route as IEnumerable<IHttpRoute>;
        if (routeCollection != null)
        {
            AddConstraint(routeCollection, key, constraint);
        }
    }
}

这在Global.asax Application_Start中调用:

// ...
GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configure(WebApiConfig.RegisterRouteConstraints);
// ...