HttpControllerDispatcher

时间:2015-06-03 19:26:00

标签: asp.net-web-api asp.net-web-api2 asp.net-web-api-routing

在我的WebApiConfig :: Register(...)中,我用自己的控制器选择器替换了HttpControllerSelector。当我发出POST请求时,正确调用了SelectController成员,并返回一个具有正确控制器类型的ControllerDescriptor。 但是HttpControllerDispatcher正在提出一个异常,说“字典中没有给出”。任何人都知道如何调试这样的错误?

完整的例外是消息:

The given key was not present in the dictionary.","ExceptionType":"System.Collections.Generic.KeyNotFoundException","StackTrace":"   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)\r\n   
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.FindActionMatchRequiredRouteAndQueryParameters(IEnumerable`1 candidatesFound)\r\n   
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.FindMatchingActions(HttpControllerContext controllerContext, Boolean ignoreVerbs)\r\n   
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n   
at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n   
at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n   
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"

这是我的控制器选择器:

public class Namespace_HTTP_Controller_Selector : IHttpControllerSelector
{
    private readonly HttpConfiguration _configuration;
    private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controller;

    public Namespace_HTTP_Controller_Selector(HttpConfiguration Config)
    {
        _configuration = Config;
        _controller = new Lazy<Dictionary<string, HttpControllerDescriptor>>(Initialize_Controller_Dictionary);
    }

    public HttpControllerDescriptor SelectController(HttpRequestMessage Request)
    {
        var Route_Data = Request.GetRouteData();
        if(Route_Data == null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }

        var Controller_Name = Get_Controller_Name(Route_Data);
        if(Controller_Name ==  null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }

        var Name_Space = Get_Version(Route_Data);
        if (Name_Space == null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }

        var Controller_Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", Name_Space, Controller_Name);

        HttpControllerDescriptor Descriptor;
        if(_controller.Value.TryGetValue(Controller_Key, out Descriptor))
        {
            return Descriptor;
        }

        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
    {
        return _controller.Value;
    }


    private Dictionary<string, HttpControllerDescriptor> Initialize_Controller_Dictionary()
    {
        var Dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);

        var Assemblies_Resolver = _configuration.Services.GetAssembliesResolver();
        var Controller_Resolver = _configuration.Services.GetHttpControllerTypeResolver();

        var Controller_Types = Controller_Resolver.GetControllerTypes(Assemblies_Resolver);

        foreach(var ct in Controller_Types)
        {
            var Segments = ct.Namespace.Split(Type.Delimiter);

            var Controller_Name = ct.Name.Remove(ct.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);

            var Controller_Key = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", Segments[Segments.Length - 1], Controller_Name);


            if(Dictionary.Keys.Contains(Controller_Key) == false)
            {
                Dictionary[Controller_Key] = new HttpControllerDescriptor(_configuration, ct.Name, ct);
            }
        }

        return Dictionary;
    }

    private T Get_Route_Variable<T>(IHttpRouteData Route_Data, string Name)
    {
        object Result;

        if(Route_Data.Values.TryGetValue(Name, out Result))
        {
            return (T)Result;
        }

        return default(T);
    }


    private string Get_Controller_Name(IHttpRouteData Route_Data)
    {

        var SubRoute = Route_Data.GetSubRoutes().FirstOrDefault();

        if( SubRoute == null )
        {
            return null;
        }

        var Data_Token_Value = SubRoute.Route.DataTokens.First().Value;
        if(Data_Token_Value == null)
        {
            return null;
        }


        var Controller_Name = ((HttpActionDescriptor[])Data_Token_Value).First().ControllerDescriptor.ControllerName.Replace("Controller", string.Empty);


        return Controller_Name;
    }

    private string Get_Version(IHttpRouteData Route_Data)
    {
        var Sub_Route_Data = Route_Data.GetSubRoutes().FirstOrDefault();
        if(Sub_Route_Data== null)
        {
            return null;
        }

        return Get_Route_Variable<string>(Sub_Route_Data, "apiVersion");
    }
}

2 个答案:

答案 0 :(得分:3)

这是因为在返回描述符之前,您没有在请求中设置子路由数据。

HttpControllerDescriptor Descriptor;
if(_controller.Value.TryGetValue(Controller_Key, out Descriptor))
{
    var subRoutes = Route_Data.GetSubRoutes();
    IEnumerable<IHttpRouteData> filteredSubRoutes = subRoutes.Where(attrRouteData =>
    {
        HttpControllerDescriptor currentDescriptor = ((HttpActionDescriptor[])Route_Data.Route.DataTokens["actions"]).First().ControllerDescriptor;
        return currentDescriptor != null && currentDescriptor.ControllerName.Equals(Descriptor.ControllerName, StringComparison.OrdinalIgnoreCase);
    });
    Route_Data.Values["MS_SubRoutes"] = filteredSubRoutes.ToArray();
    return Descriptor;
}

看看: Versioning ASP.NET Web API 2 with Media Types

答案 1 :(得分:0)

public class CustomSelectorController : DefaultHttpControllerSelector
    {
        HttpConfiguration _config;

        public CustomSelectorController(HttpConfiguration config) : base(config)
        {
            _config = config;
        }

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            IHttpRouteData routeData = request.GetRouteData();
            IEnumerable<IHttpRouteData> attributeSubRoutes = routeData.GetSubRoutes();

            var actions = attributeSubRoutes.LastOrDefault()?.Route?.DataTokens["actions"] as HttpActionDescriptor[];

            var controllerName = "";
            if (actions != null && actions.Length > 0)
            {
                controllerName = actions[0].ControllerDescriptor.ControllerName;
            }

            IEnumerable<string> headerValues = null;
            //Custom Header Name to be check version
            if (request.Headers.TryGetValues("Accept-Version", out headerValues))
            {
                var apiVersion = headerValues.First().ToUpper();
                if (apiVersion == "V2")
                {
                    controllerName = controllerName + apiVersion;
                }
            }

            HttpControllerDescriptor controllerDescriptor = null;

            IEnumerable<IHttpRouteData> filteredSubRoutes = attributeSubRoutes.Where(attrRouteData =>
            {
                HttpControllerDescriptor currentDescriptor = GetControllerDescriptor(attrRouteData);

                bool match = currentDescriptor.ControllerName.Equals(controllerName);

                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;
        }
    }