【问题标题】:WebAPI: custom routing to asynchronously loaded ApiControllerWebAPI:自定义路由到异步加载的 ApiController
【发布时间】:2014-02-13 14:10:43
【问题描述】:

我以前在以前的职位上做过这个,但我不记得是怎么做的了。

我有一个基于插件的 WebAPI 应用程序。每个插件程序集都有一个实现IApiServiceEntryPoint 的类,类似于:

public interface IApiServiceEntryPoint : IDisposable
{
    /// <summary>
    /// Gets the name of the API Plugin
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Registers the assembly in the application, sets up the routes, and enables invocation of API requests
    /// </summary>
    void Register(RouteCollection routes);

    /// <summary>
    /// Gets the routing namespace of the plugin
    /// </summary>
    string UrlNameSpace { get; }
 }

Register(RouteCollection routes) 除其他外,从主 Web 应用程序 (MVC4) 获取路由集合,并添加此插件程序集将支持的自定义路由。

假设我在新的MyApi.Plugins.Foo 程序集中的MyApi.Plugins.Foo.FooServiceEntryPoint 类中实现了这个接口。 Name 的值是“Foo”,UrlNameSpace 是“Foo”,假设 api 控制器类是 MyApi.Plugins.Foo.FooController。其意图是当消费者点击http://myapi.something.com/Foo/GiveMeRecords 时,将调用MyApi.Plugins.Foo.FooController.GiveMeRecords 方法。

我的MyApi.Plugins.Foo.FooServiceEntryPoint.Register(RouteCollection routes) 应该是什么样的?

【问题讨论】:

    标签: c# asp.net-mvc asp.net-web-api routes


    【解决方案1】:

    好的,我想通了。这比我最初想象的要复杂一些,但需要做的是:

    Register方法中,我把下面的代码放在一个实现IApiServiceEntryPoint的抽象类中:

    public virtual void Register(RouteCollection routes)
    {
        var rt = string.Format("{0}/{{controller}}/{{id}}", UrlNameSpace);
        var nameSpace = this.GetType().Namespace;
        Logger.DebugFormat("Route Template: {0} in namespace {1}...", rt, nameSpace);
        var r = routes.MapHttpRoute(
            name: Name,
            routeTemplate: rt,
            defaults: new { id = RouteParameter.Optional, controller = "Default", @namespace = nameSpace }
            );
        r.DataTokens = r.DataTokens ?? new RouteValueDictionary();
        r.DataTokens["Namespaces"] = new[] { nameSpace };
    
        Logger.InfoFormat("Plugin '{0}' registered namespace '{1}'.", Name, nameSpace);
    }
    

    然后我不得不实现我自己的IHttpControllerSelector(我无耻地从我发现here的博客文章中修改了它):

    public class ApiHttpControllerSelector : IHttpControllerSelector
    {
        private const string NamespaceKey = "namespace";
        private const string ControllerKey = "controller";
    
        private readonly HttpConfiguration _configuration;
        private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
        private readonly HashSet<string> _duplicates;
        private static readonly List<Type> _pluginControllers = new List<Type>();
    
        public ApiHttpControllerSelector(HttpConfiguration config)
        {
            _configuration = config;
            _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
            _pluginControllers.AddRange(ApiFramework.PluginHelper.Controllers);
        }
    
        private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
        {
            var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
    
            var assembliesResolver = _configuration.Services.GetAssembliesResolver();
            var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
    
            var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver)
                .Concat(_pluginControllers).ToList();               
    
            foreach (var t in controllerTypes)
            {
                var key = string.Format("{0}.{1}", t.Namespace, t.Name);
                dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
            }
            return dictionary;
        }
    
        public System.Web.Http.Controllers.HttpControllerDescriptor SelectController(System.Net.Http.HttpRequestMessage request)
        {
            var routeData = request.GetRouteData();
            if (routeData == null)
            {
                throw new HttpResponseException(new HttpResponseMessage(System.Net.HttpStatusCode.NotFound));
            }
    
            string namespaceName = GetRouteVariable<string>(routeData, "namespace");
            if (namespaceName == null)
            {
                throw new HttpResponseException(new HttpResponseMessage(System.Net.HttpStatusCode.NotFound));
            }
    
            string controllerName = GetRouteVariable<string>(routeData, "controller");
            if (controllerName == null)
            {
                throw new HttpResponseException(new HttpResponseMessage(System.Net.HttpStatusCode.NotFound));
            }
    
            // Find a matching controller.
            var baseKey = String.Format(CultureInfo.InvariantCulture, "{0}.{1}Controller", namespaceName, controllerName);
            var key = String.Format("{0}Controller", baseKey);
            HttpControllerDescriptor controllerDescriptor;
            if (_controllers.Value.TryGetValue(key, out controllerDescriptor)) // the default should include the "Controller" at the end of the class name
            {
                return controllerDescriptor;
            }
            else if (_controllers.Value.TryGetValue(baseKey, out controllerDescriptor)) //explicit class name, sans "Controller"
            {
                return controllerDescriptor;
            }
            else if (_duplicates.Contains(key))
            {
                throw new HttpResponseException(
                    request.CreateErrorResponse(HttpStatusCode.InternalServerError,
                    "Multiple controllers were found that match this request."));
            }
            else
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
    
        }
    
        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);
        }
    
        public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
        {
            return _controllers.Value;
        }
    }
    

    然后我必须像这样替换我的Global.asax.cs 中的默认IHttpControllerSelector

    GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
                new ApiHttpControllerSelector(GlobalConfiguration.Configuration));
    

    ...然后一切正常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-05
      • 1970-01-01
      • 1970-01-01
      • 2015-03-19
      相关资源
      最近更新 更多