通过《ASP.NET Web API的Controller是如何被创建的?》的介绍我们知道默认ASP.NET Web API在Self Host寄宿模式下用于解析程序集的AssembliesResolver是一个DefaultAssembliesResolver对象,它只会提供当前应用程序域已经加载的程序集。如果我们将HttpController定义在非寄宿程序所在的程序集中(实际上在采用Self Host寄宿模式下,我们基本上都会选择在独立的项目定义HttpController类型),即使我们将它们部属在宿主程序运行的目录中,宿主程序启动的时候也不会主动去加载这些程序集。由于当前应用程序域中并不曾加载这些程序集,HttpController类型解析将会失败,HttpController的激活自然就无法实现。[本文已经同步到《How ASP.NET Web API Works?》]

我们可以通过一个简单的实例来证实这个问题。我们在一个解决方案中定义了如右图所示的4个项目,其中Foo、Bar和Baz为类库项目,相应的HttpController类型就定义在这3个项目之中。Hosting是一个作为宿主的控制台程序,它具有对上述3个项目的引用。我们分别在项目Foo、Bar和Baz中定义了三个继承自ApiController的HttpController类型FooController、BarController和BazController。如下面的代码片断所示,我们在这3个HttpController类型中定义了唯一的Action方法Get并让它返回当前HttpController类型的AssemblyQualifiedName。

   1: public class FooController : ApiController

   2: {

   3:     public string Get()

   4:     {

   5:         return this.GetType().AssemblyQualifiedName;

   6:     }

   7: }

   8:  

   9: public class BarController : ApiController

  10: {

  11:     public string Get()

  12:     {

  13:         return this.GetType().AssemblyQualifiedName;

  14:     }

  15: }

  16:  

  17: public class BarController : ApiController

  18: {

  19:     public string Get()

  20:     {

  21:         return this.GetType().AssemblyQualifiedName;

  22:     }

  23: }

我们在作为宿主的Hosting程序中利用如下的代码以Self Host模式实现了针对Web API的寄宿。我们针对基地址“http://127.0.0.1:3721”创建了一个HttpSelfHostServer,在开启之前我们注册了一个URL模板为“api/{controller}/{id}”的路由。

   1: class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:         Uri baseAddress = new Uri("http://127.0.0.1:3721");

   6:         using (HttpSelfHostServer httpServer = new HttpSelfHostServer(new HttpSelfHostConfiguration(baseAddress)))

   7:         {

   8:             httpServer.Configuration.Routes.MapHttpRoute(

   9:                 name             : "DefaultApi",

  10:                 routeTemplate    : "api/{controller}/{id}",

  11:                 defaults         : new { id = RouteParameter.Optional });

  12:  

  13:             httpServer.OpenAsync().Wait();

  14:             Console.Read();

  15:         }

  16:     }

  17: }

在启动宿主程序后,我们试图通过浏览器对分别定义在FooController、BarController和BazController中的Action方法Get发起调用,不幸的是我们会得到如图4-4所示的结果。从显示在浏览器中的消息我们很清楚问题的症结所在:根据路由解析得到HttpController名称并不能得到匹配的类型。

导致上述这个问题的原因我们在上面已经分析过了:默认注册的DefaultAssembliesResolver仅仅提供当前应用程序域加载的程序集。我们可以通过自定义的AssembliesResolver来解决这个问题。我们的解决思路是让需要预先加载的程序集可配置,具体来说可以采用具有如下结构的配置来设置需要预先加载的程序集。

   1: <configuration>

   2:    <configSections>

   3:      <section name="preLoadedAssemblies" 

   4:               type="Hosting.PreLoadedAssembliesSettings, Hosting"/>

   5:    </configSections>

   6: <preLoadedAssemblies>

   7:   <add assemblyName ="Foo.dll"/>

   8:   <add assemblyName ="Bar.dll"/>

   9:   <add assemblyName ="Baz.dll"/>

  10: </preLoadedAssemblies>

  11: </configuration>

在创建自定义的AssembliesResolver之前我们先得为这段配置定义相应的配置节和配置元素类型。相关的类型(PreLoadedAssembliesSettings、AssemblyElementCollection和AssemblyElement)定义如下所示,由于配置结构比较简单,在这里我们不对它们作详细介绍了。

   1: public class PreLoadedAssembliesSettings: ConfigurationSection

   2: {

   3:     [ConfigurationProperty("", IsDefaultCollection = true)]

   4:     public AssemblyElementCollection AssemblyNames

   5:     {

   6:         get { return (AssemblyElementCollection)this[""]; }

   7:     }

   8:  

   9:     public static PreLoadedAssembliesSettings GetSection()

  10:     {

  11:         return ConfigurationManager.GetSection("preLoadedAssemblies") 

  12:             as PreLoadedAssembliesSettings;

  13:     }

  14: }

  15:  

  16: public class AssemblyElementCollection : ConfigurationElementCollection

  17: {

  18:     protected override ConfigurationElement CreateNewElement()

  19:     {

  20:         return new AssemblyElement();

  21:     }

  22:     protected override object GetElementKey(ConfigurationElement element)

  23:     {

  24:         AssemblyElement serviceTypeElement = (AssemblyElement)element;

  25:         return serviceTypeElement.AssemblyName;

  26:     }

  27: }

  28:     

  29: public class AssemblyElement : ConfigurationElement

  30: {

  31:     [ConfigurationProperty("assemblyName", IsRequired = true)]

  32:     public string AssemblyName

  33:     {

  34:         get { return (string)this["assemblyName"]; }

  35:         set { this["assemblyName"] = value; }

  36:     }

  37: }

由于我们自定义的AssembliesResolver是对现有DefaultAssembliesResolver的扩展(尽管其程序集提供机制仅仅通过一句代码来实现),我们将类型命名为ExtendedDefaultAssembliesResolver。如下面的代码片断所示,ExtendedDefaultAssembliesResolver继承自DefaultAssembliesResolver,在重写的GetAssemblies方法中我们先通过分析上述的配置并主动加载尚未加载的程序集,然后调用基类的同名方法来提供最终的程序集。

   1: public class ExtendedDefaultAssembliesResolver : DefaultAssembliesResolver

   2: {

   3:     public override ICollection<Assembly> GetAssemblies()

   4:     {

   5:         PreLoadedAssembliesSettings settings = PreLoadedAssembliesSettings.GetSection();

   6:         if (null != settings)

   7:         {

   8:             foreach (AssemblyElement element in settings.AssemblyNames)

   9:             {

  10:                 AssemblyName assemblyName = AssemblyName.GetAssemblyName(element.AssemblyName);

  11:                 if(!AppDomain.CurrentDomain.GetAssemblies().Any(assembly=>AssemblyName.ReferenceMatchesDefinition(assembly.GetName(),assemblyName)))

  12:                 {

  13:                     AppDomain.CurrentDomain.Load(assemblyName);

  14:                 }

  15:             }

  16:         }

  17:         return base.GetAssemblies();

  18:     }

  19: }

我们在作为宿主的Hosting程序中利用如下的代码将一个ExtendedDefaultAssembliesResolver对象注册到当前HttpConfiguration的ServicesContainer上。

   1: class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:         Uri baseAddress = new Uri("http://127.0.0.1:3721");

   6:         using (HttpSelfHostServer httpServer = new HttpSelfHostServer(new HttpSelfHostConfiguration(baseAddress)))

   7:         {

   8:             httpServer.Configuration.Services.Replace(typeof(IAssembliesResolver),new ExtendedDefaultAssembliesResolver());

   9:             //其他操作

  10:         }

  11:     }

  12: }

重新启动宿主程序后再次在浏览器输入对应的地址来访问分别定义在FooController、BarController和BazController中的Action方法Get,我们会得到如下图所示的输出结果,这正是目标Action方法执行的结果。

[ASP.NET Web API]如何Host定义在独立程序集中的Controller的更多相关文章

  1. asp.net web api 2 host in a windows service推荐阅读

    最简单的例子(官方)在控制台app里面运行: http://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-h ...

  2. How ASP.NET Web API 2.0 Works?[持续更新中…]

    一.概述 RESTful Web API [Web标准篇]RESTful Web API [设计篇] 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 二.路由 ...

  3. 【ASP.NET Web API教程】3.2 通过.NET客户端调用Web API(C#)

    原文:[ASP.NET Web API教程]3.2 通过.NET客户端调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

  4. 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)

    原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

  5. Self-Host c#学习笔记之Application.DoEvents应用 不用IIS也能執行ASP.NET Web API

    Self-Host   寄宿Web API 不一定需要IIS 的支持,我们可以采用Self Host 的方式使用任意类型的应用程序(控制台.Windows Forms 应用.WPF 应用甚至是Wind ...

  6. 打造属于自己的支持版本迭代的Asp.Net Web Api Route

    在目前的主流架构中,我们越来越多的看到web Api的存在,小巧,灵活,基于Http协议,使它在越来越多的微服务项目或者移动项目充当很好的service endpoint. 问题 以Asp.Net W ...

  7. ASP.NET Web API路由系统:Web Host下的URL路由

    ASP.NET Web API提供了一个独立于执行环境的抽象化的HTTP请求处理管道,而ASP.NET Web API自身的路由系统也不依赖于ASP.NET路由系统,所以它可以采用不同的寄宿方式运行于 ...

  8. Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?

    构成ASP.NET Web API核心框架的消息处理管道既不关心请求消息来源于何处,也不需要考虑响应消息归于何方.当我们采用Web Host模式将一个ASP.NET应用作为目标Web API的宿主时, ...

  9. ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇]

    ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇] 我们知道ASP.NET Web API借助于HttpSelfHostServer以Self Host模式寄宿于当 ...

随机推荐

  1. 安卓手持智能POS端上能扫描开单的软件-店面销售开单系统

    安卓移动POS终端上能扫描开单的软件: 一.登录验证,以业务员.密码登录: 二.订单列表,显示相关信息,包括日期.单据号.客户名.金额.单据状态.有新增订单及按特定条件查询订单的功能(日期.客户等). ...

  2. Java基础加强之集合篇(模块记忆、精要分析)

    千里之行,始于足下.把别人的变成自己,再把自己的分享给别人,这也是一次提升的过程.本文的目的是以一篇文章从整体把握集合体系又不失一些细节上的实现,高手路过. 集合的作用与特点 Java是一门面向对象语 ...

  3. linux查看是什么操作系统是什么命令

    https://zhidao.baidu.com/question/361519585968730492.html

  4. .NET Web开发笔记

    HttpWebRequest HttpWebRequest默认关闭Cookies,给其赋初始值以打开Cookies读写 myRequest.CookieContainer = new CookieCo ...

  5. Linux下添加apache虚拟主机

    一切在确保apache被正确安装的前提下 设置虚拟主机 创建虚拟目录 现在,让我们继续安装虚拟主机.虚拟主机命名为local.gis. 创建一个公用的文件夹来存放这虚拟主机的数据. 首先,让我们为lo ...

  6. [Protobuf] Mac系统下安装配置及简单使用

    Mac下Protobuf安装 Protobuf源码Github地址: https://github.com/google/protobuf 配置环境教程: https://github.com/goo ...

  7. git上传代码到osc@git

    1.get an account 2.get a ssh-key 3.git setting git config --global user.name "...." git co ...

  8. 通过NFS(nfsroot)启动linux系统

    Mounting the root filesystem via NFS (nfsroot) 英文原文位于inux内核源代码中的"Documentation/filesystems/nfs/ ...

  9. ArcGIS AddIN开发:如何调用ArcMap中的选择工作空间的窗体

    示例代码如下: public static IWorkspaceName BrowseWorkspace(int hwnd,out IWorkspace ws) { IGxObjectFilterCo ...

  10. hadoop分布式存储(2)-hadoop的安装(毕业设计)

    总共分三步:1.准备linux环境 租用"云主机",阿里云,unitedStack等,云主机不受本机性能影响(或者直接安转linux操作系统或者虚拟机也行): PuTTy Conf ...