c# – 使用媒体类型版本化ASP.NET Web API 2

我正在使用具有属性路由的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);
    }

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...