1

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

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

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

我知道MVC5 中的自定义路由约束,但我真的希望能够根据参数名称按约定添加一些约束。

是否可以对 MVC5.1 的属性路由功能做同样的事情?

4

1 回答 1

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 和 LinkGeneatioRoute。基于属性的路由在 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);
// ...
于 2014-04-22T14:25:53.490 回答