我正在使用带有属性路由的 ASP.NET Web API 2,但我似乎无法使用媒体类型application/vnd.company[.version].param[+json]
进行版本控制。
我收到以下错误:
字典中不存在给定的键。
这源于测试方法中的_actionParameterNames[descriptor]
关键FindActionMatchRequiredRouteAndQueryParameters()
。
foreach (var candidate in candidatesFound)
{
HttpActionDescriptor descriptor = candidate.ActionDescriptor;
if (IsSubset(_actionParameterNames[descriptor], candidate.CombinedParameterNames))
{
matches.Add(candidate);
}
}
来源:ApiControllerActionSelector.cs
经过进一步调试,我意识到如果你有两个控制器
[RoutePrefix("api/people")]
public class PeopleController : BaseApiController
{
[Route("")]
public HttpResponseMessage GetPeople()
{
}
[Route("identifier/{id}")]
public HttpResponseMessage GetPersonById()
{
}
}
[RoutePrefix("api/people")]
public class PeopleV2Controller : BaseApiController
{
[Route("")]
public HttpResponseMessage GetPeople()
{
}
[Route("identifier/{id}")]
public HttpResponseMessage GetPersonById()
{
}
}
你不能使用你的自定义ApiVersioningSelector : DefaultHttpControllerSelector
,因为它会测试键,如上所述,来自所有具有相同的控制器,[RoutePrefix("api/people")]
显然会抛出异常。
只是为了确保选择了正确的控制器
我不知道这是否是一个错误,但使用路由[RoutePrefix("api/v1/people")] to version API
让我很难过。
注意:这在没有属性路由的情况下效果很好。
更新
public class ApiVersioningSelector : DefaultHttpControllerSelector
{
private HttpConfiguration _HttpConfiguration;
public ApiVersioningSelector(HttpConfiguration httpConfiguration)
: base(httpConfiguration)
{
_HttpConfiguration = httpConfiguration;
}
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IDictionary<string, HttpControllerDescriptor> controllers = GetControllerMapping();
var attributedRoutesData = request.GetRouteData().GetSubRoutes();
var subRouteData = attributedRoutesData.LastOrDefault(); //LastOrDefault() will get PeopleController, FirstOrDefault will get People{version}Controller which we don't want
var actions = (ReflectedHttpActionDescriptor[])subRouteData.Route.DataTokens["actions"];
var controllerName = actions[0].ControllerDescriptor.ControllerName;
//For controller name without attribute routing
//var controllerName = (string)routeData.Values["controller"];
HttpControllerDescriptor oldControllerDescriptor;
if (controllers.TryGetValue(controllerName, out oldControllerDescriptor))
{
var apiVersion = GetVersionFromMediaType(request);
var newControllerName = String.Concat(controllerName, "V", apiVersion);
HttpControllerDescriptor newControllerDescriptor;
if (controllers.TryGetValue(newControllerName, out newControllerDescriptor))
{
return newControllerDescriptor;
}
return oldControllerDescriptor;
}
return null;
}
private string GetVersionFromMediaType(HttpRequestMessage request)
{
var acceptHeader = request.Headers.Accept;
var regularExpression = new Regex(@"application\/vnd\.mycompany\.([a-z]+)\.v([0-9]+)\+json",
RegexOptions.IgnoreCase);
foreach (var mime in acceptHeader)
{
var match = regularExpression.Match(mime.MediaType);
if (match != null)
{
return match.Groups[2].Value;
}
}
return "1";
}
}