我正在使用具有属性路由的ASP.NET Web API 2,但我似乎无法使用媒体类型application / vnd.company [.version] .param [json]获得版本控制.
我收到以下错误:
The given key was not present in the dictionary.
它来源于FindActionMatchrequiredRouteAndQueryParameters()方法中的关键_actionParameterNames [descriptor].
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”)]到版本API让我很伤心.
注意:没有属性路由,这很有效.
UPDATE
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"; } }
解决方法
感谢您分享您的代码.我已经修改了您的版本控制器选择器,如下所示,并尝试了一些情况,它似乎工作正常.您可以尝试如下更新您的控制器选择器,看看它是否有效?
public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { HttpControllerDescriptor controllerDescriptor = null; // get list of all controllers provided by the default selector IDictionary<string,HttpControllerDescriptor> controllers = GetControllerMapping(); IHttpRouteData routeData = request.GetRouteData(); if (routeData == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } //check if this route is actually an attribute route IEnumerable<IHttpRouteData> attributeSubRoutes = routeData.GetSubRoutes(); var apiVersion = GetVersionFromMediaType(request); if (attributeSubRoutes == null) { string controllerName = GetRouteVariable<string>(routeData,"controller"); if (controllerName == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } string newControllerName = String.Concat(controllerName,apiVersion); if (controllers.TryGetValue(newControllerName,out controllerDescriptor)) { return controllerDescriptor; } else { throw new HttpResponseException(HttpStatusCode.NotFound); } } else { // we want to find all controller descriptors whose controller type names end with // the following suffix(ex: CustomersV1) string newControllerNameSuffix = String.Concat("V",apiVersion); IEnumerable<IHttpRouteData> filteredSubRoutes = attributeSubRoutes.Where(attrRouteData => { HttpControllerDescriptor currentDescriptor = GetControllerDescriptor(attrRouteData); bool match = currentDescriptor.ControllerName.EndsWith(newControllerNameSuffix); if (match && (controllerDescriptor == null)) { controllerDescriptor = currentDescriptor; } return match; }); routeData.Values["MS_SubRoutes"] = filteredSubRoutes.ToArray(); } return controllerDescriptor; } private HttpControllerDescriptor GetControllerDescriptor(IHttpRouteData routeData) { return ((HttpActionDescriptor[])routeData.Route.DataTokens["actions"]).First().ControllerDescriptor; } // Get a value from the route data,if present. private static T GetRouteVariable<T>(IHttpRouteData routeData,string name) { object result = null; if (routeData.Values.TryGetValue(name,out result)) { return (T)result; } return default(T); }