Asp.net web api ASP.NET Web API RTM和子域路由
我在使用基于子域的路由时遇到问题。简而言之,我使用的是正确的控制器和方法,但是WebAPI没有获取子域外的数据令牌 在我的场景中,我有以下内容:Asp.net web api ASP.NET Web API RTM和子域路由,asp.net-web-api,asp.net-mvc-routing,Asp.net Web Api,Asp.net Mvc Routing,我在使用基于子域的路由时遇到问题。简而言之,我使用的是正确的控制器和方法,但是WebAPI没有获取子域外的数据令牌 在我的场景中,我有以下内容: contoso.myapp.com fabrikam.myapp.com {tenant}.myapp.com 所有解析到同一个ApiController,我希望能够提取{tenant}令牌 我在本文中使用了代码 但在撰写本文和ASP.NETWebAPI退出测试版之间,似乎有些东西发生了变化。本文中的代码依赖于RouteTable.Routes,而在
contoso.myapp.com
fabrikam.myapp.com
{tenant}.myapp.com
所有解析到同一个ApiController,我希望能够提取{tenant}
令牌
我在本文中使用了代码
但在撰写本文和ASP.NETWebAPI退出测试版之间,似乎有些东西发生了变化。本文中的代码依赖于RouteTable.Routes
,而在Web API上,路由是通过HttpConfiguration.Routes
配置的,它是一个HttpRouteCollection
,而不是通常的RouteCollection
(它实际上源于RouteCollection
)
因此,我将代码更改为派生自HttpRoute
,而不是Route
。代码如下:
我像这样配置一个路由
config.Routes.Add(new HttpDomainRoute(
name: "test",
domain: "{tenant}.myapp.com",
routeTemplate: "test",
defaults: new { controller = "SomeController", action = "Test" }
));
我的请求被路由到正确的控制器。但是,租户数据令牌永远不会被填充(如果我执行this.Request.GetRouteData()
操作,我会看到控制器和操作令牌,但不会看到租户)。如果我在GetRouteData
上设置断点,则永远不会调用它
我尝试使用reflector跟踪代码路径,查看在HttpRouteCollection级别调用GetRouteData的位置,但似乎集合枚举为空。不确定常规ASP.NET路由和WEb API路由之间的集成是如何桥接的,但这让我感到困惑
有什么想法吗?
我现在使用的解决方法是通过路由显式调用GetRouteData
this.Request.GetRouteData().Route.GetRouteData(this.Request.RequestUri.ToString(),this.Request)
感谢您报告此问题。我用了你的程序并做了一些调试
这就是它发生的原因。未调用HttpDomainRoute.GetRouteData
,因为它是由Web API中名为HttpWebRoute
的内部类包装的。当您使用config.Routes.Add
方法添加自定义路由时,它不调用HttpDomainRoute.GetRouteData
,只调用System.Web.Routing.route的实现GetRouteData
。这就是为什么除了租户之外,其余参数都被正确映射的原因
我想不出任何简单的解决办法。我可以在的codeplex站点上提交问题以跟踪此问题 再想一想,我有一个解决办法给你。解决方法的基本思想是使用从route派生的route,并将其直接添加到RouteCollection。这样,我们通过的路由将不再被包装在HttpWebRoute中
RouteByPassing处理程序用于解决另一个已知问题。希望这有帮助
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
RouteTable.Routes.Add("test", new HttpDomainRoute(
domain: "{tenant}.auth10.com",
routeTemplate: "test",
defaults: new { controller = "Values", action = "GetTenant" }
));
config.MessageHandlers.Add(new RouteByPassingHandler());
}
}
public class RouteByPassingHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
HttpMessageInvoker invoker = new HttpMessageInvoker(new HttpControllerDispatcher(request.GetConfiguration()));
return invoker.SendAsync(request, cancellationToken);
}
}
public class HttpDomainRoute
: Route
{
private Regex domainRegex;
private Regex pathRegex;
public HttpDomainRoute(string domain, string routeTemplate, object defaults, object constraints = null)
: base(routeTemplate, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(), HttpControllerRouteHandler.Instance)
{
this.Domain = domain;
}
public string Domain { get; set; }
public override RouteData GetRouteData(HttpContextBase context)
{
// Build regex
domainRegex = CreateRegex(this.Domain);
pathRegex = CreateRegex(this.Url);
// Request information
string requestDomain = context.Request.Headers["Host"];
if (!string.IsNullOrEmpty(requestDomain))
{
if (requestDomain.IndexOf(":") > 0)
{
requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":"));
}
}
else
{
requestDomain = context.Request.Url.Host;
}
string requestPath = context.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + context.Request.PathInfo;
// Match domain and route
Match domainMatch = domainRegex.Match(requestDomain);
Match pathMatch = pathRegex.Match(requestPath);
// Route data
RouteData data = null;
if (domainMatch.Success && pathMatch.Success)
{
data = base.GetRouteData(context);
// Add defaults first
if (Defaults != null)
{
foreach (KeyValuePair<string, object> item in Defaults)
{
data.Values[item.Key] = item.Value;
}
}
// Iterate matching domain groups
for (int i = 1; i < domainMatch.Groups.Count; i++)
{
Group group = domainMatch.Groups[i];
if (group.Success)
{
string key = domainRegex.GroupNameFromNumber(i);
if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
{
if (!string.IsNullOrEmpty(group.Value))
{
data.Values[key] = group.Value;
}
}
}
}
// Iterate matching path groups
for (int i = 1; i < pathMatch.Groups.Count; i++)
{
Group group = pathMatch.Groups[i];
if (group.Success)
{
string key = pathRegex.GroupNameFromNumber(i);
if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
{
if (!string.IsNullOrEmpty(group.Value))
{
data.Values[key] = group.Value;
}
}
}
}
}
return data;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
}
private Regex CreateRegex(string source)
{
// Perform replacements
source = source.Replace("/", @"\/?");
source = source.Replace(".", @"\.?");
source = source.Replace("-", @"\-?");
source = source.Replace("{", @"(?<");
source = source.Replace("}", @">([a-zA-Z0-9_-]*))");
return new Regex("^" + source + "$");
}
private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
{
Regex tokenRegex = new Regex(@"({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?({[a-zA-Z0-9_-]*})*-?\.?\/?");
Match tokenMatch = tokenRegex.Match(Domain);
for (int i = 0; i < tokenMatch.Groups.Count; i++)
{
Group group = tokenMatch.Groups[i];
if (group.Success)
{
string key = group.Value.Replace("{", "").Replace("}", "");
if (values.ContainsKey(key))
values.Remove(key);
}
}
return values;
}
}
公共静态类WebApiConfig
{
公共静态无效寄存器(HttpConfiguration配置)
{
config.Routes.MapHttpRoute(
名称:“DefaultApi”,
routeTemplate:“api/{controller}/{id}”,
默认值:新建{id=RouteParameter.Optional}
);
RouteTable.Routes.Add(“测试”,新的HttpDomainRoute(
域:“{tenant}.auth10.com”,
routeTemplate:“测试”,
默认值:新建{controller=“Values”,action=“GetTenant”}
));
添加(新的RouteByPassingHandler());
}
}
公共类RouteByPassingHandler:DelegatingHandler
{
受保护的重写System.Threading.Tasks.Task SendAsync(HttpRequestMessage请求,System.Threading.CancellationToken CancellationToken)
{
HttpMessageInvoker=新的HttpMessageInvoker(新的HttpControllerDispatcher(request.GetConfiguration());
返回invoker.sendaync(请求、取消令牌);
}
}
公共类HttpDomainRoute
:路线
{
私有Regex域Regex;
私有正则表达式pathRegex;
公共HttpDomainRoute(字符串域,字符串routeTemplate,对象默认值,对象约束=null)
:base(routeTemplate、new RouteValueDictionary(默认)、new RouteValueDictionary(约束)、new RouteValueDictionary()、HttpControllerRouteHandler.Instance)
{
this.Domain=域;
}
公共字符串域{get;set;}
公共覆盖路由数据GetRouteData(HttpContextBase上下文)
{
//构建正则表达式
domainRegex=CreateRegex(this.Domain);
pathRegex=CreateRegex(this.Url);
//请求信息
字符串requestDomain=context.Request.Headers[“Host”];
如果(!string.IsNullOrEmpty(requestDomain))
{
if(requestDomain.IndexOf(“:”)大于0)
{
requestDomain=requestDomain.Substring(0,requestDomain.IndexOf(“:”);
}
}
其他的
{
requestDomain=context.Request.Url.Host;
}
字符串requestPath=context.Request.AppRelativeCurrentExecutionFilePath.Substring(2)+context.Request.PathInfo;
//匹配域和路由
Match domainMatch=domainRegex.Match(requestDomain);
Match-pathMatch=pathRegex.Match(请求路径);
//路由数据
RoutedData=null;
if(domainMatch.Success&&pathMatch.Success)
{
data=base.GetRouteData(上下文);
//首先添加默认值
if(默认值!=null)
{
foreach(默认值中的KeyValuePair项)
{
data.Values[item.Key]=item.Value;
}
}
//迭代匹配域组
对于(int i=1;i