我有同样的需求并找到了解决方案,虽然我没有彻底测试过。我的场景是一个多租户 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);
// ...