我尝试为我的Web API 2.2项目使用自定义控制器选择器,该项目使用名称空间选择进行版本控制。该项目是自托管。
互联网上有很多关于此的文章,但大多数只涉及一个用例(即传统的路由,或者只有属性路由,但没有路由前缀等)。
在我的情况下,我想使用所有的cookie。 换句话说,我的选择器必须做的是支持这些:
namespace WebProject.Controllers.v1
{
[RoutePrefix("accounts")]
public class AccountsController : ApiController
{
[Route("")]
public IHttpActionResult Get()
{
var accounts = accountList.ConvertAll(LogicMgr.ConvertModelToView);
return Ok(accounts);
}
}
}
namespace WebProject.Controllers.v2
{
[RoutePrefix("accounts")]
public class AccountsController : ApiController
{
[Route("")]
public IHttpActionResult Get()
{
var accounts = accountList.ConvertAll(LogicMgr.ConvertModelToView);
return Ok(accounts);
}
}
}
任何选择器的主要部分是从请求中提取控制器名称。在大多数文章中,这是通过带有特定键的字典(" controller")来完成的。但这仅适用于传统的模板路由。
处理属性路由时,建议使用路由约束并保留默认控制器选择器。不适合我。
目前我提取控制器名称如下:
static string GetControllerName(IHttpRouteData route)
{
var controller = route.GetSubRoutes()
.Select(s => new
{
Name = ((HttpActionDescriptor[])s.Route.DataTokens.Single(t => t.Value is HttpActionDescriptor[]).Value)
.FirstOrDefault().ControllerDescriptor.ControllerName
}).FirstOrDefault();
return controller.Name;
}
对我来说似乎有点hacky,但它有效并且不使用任何魔术常量(比如使用"动作"作为搜索字典中的键)。
反正。在构建控制器列表(通过解析器),从请求中提取控制器名称并根据版本找到正确的HttpControllerDescriptor之后,我需要从SelectController()方法返回它。
这就是我得到KeyNotFoundException异常的地方。 搜索后我发现只有一个提到这个(Exception in HttpControllerDispatcher)
解决方案是在传入请求中填写正确的子路由数据。现在我还没有在互联网上找到这个或任何其他的官方提及。只有这篇文章。
我想出的是另一个hacky代码:
static void FillSubRoutes(IHttpRouteData route, string version)
{
var subroutes = route.GetSubRoutes()
.Where(s =>
((HttpActionDescriptor[])s.Route.DataTokens.Single(t => t.Value is HttpActionDescriptor[]).Value)
.Any(d => d.ControllerDescriptor.ControllerType.Namespace.EndsWith(version, StringComparison.OrdinalIgnoreCase)));
var values = route.Values.Single(v => v.Value is IHttpRouteData[]);
route.Values[values.Key] = subroutes.ToArray();
}
最初路由在我的情况下有4个子路由(每个控制器版本2个)。 这里发生的是我在末尾部分搜索具有特定版本的控制器描述符的子路由器的控制器命名空间。在我的情况下,它总是2个(一个用于" GET和#34;一个用于" POST和#34;请求,为了简单起见,我在之前的控制器示例中省略了)。
所以最后我得到了我的问题: