通过《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. swift 手机号码正则表达式 记录一下

    func isTelNumber(num:NSString)->Bool { var mobile = "^1(3[0-9]|5[0-35-9]|8[025-9])\\d{8}$&qu ...

  2. Ubuntu菜鸟入门(七)—— 微信安装

    一.安装git sudo apt-get install git 设置自己的用户名和密码 sudo git config --global user.name "ABC" sudo ...

  3. Global Translator

    Global Translator插件可以把已经通过翻译服务翻译好的内容生成对应语种的“静态”页面,或者说“缓存”起来,这样在一段时间内(可设置)想访问该语种的这 个页面的访客,就可以在不调用翻译服务 ...

  4. 【Gson】互相转化

    Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库.可以将一个 JSON 字符串转成一个 Java 对象,或者反过来. 对象转为字符串 Strin ...

  5. 用Fiddler的自动响应模拟系统集成

    1. 下载最新版本的Fiddler Fiddler 官网 2, 安装并启动Fiddler 3, 勾选自动响应 见上图 4, 添加自动响应规则 见上图 5, 添加自动响应内容文件 添加响应文件到Fidd ...

  6. 使用VisualVM检测

    下载 https://visualvm.github.io/ 检测远程服务器 转自:http://blog.csdn.net/yangkangtq/article/details/52277794 授 ...

  7. linux菜鸟日记(4)

    使用一个简单的for循环和if判断语句实现某个网段内所有ping所有客户机的shell程序: ..} do >&; then echo " ${i}通" else e ...

  8. Oracle OEM

    一.OEM是什么 Oracle Enterprise Manager(Oracle企业治理器,简称OEM)是通过一组Oracle程序,为治理分布式环境提供了治理服务. 二.配置OEM 三.启动OEM ...

  9. 利用PHP的ob函数实现生成静态化页面

    之前用过一些开源的CMS管理系统,当时就很好奇后台中的生成HTML静态文件是怎么实现的.今天和同事讨论了下,没想到同事之前做过这类的生成静态页面的功能,果断向他请教了下. 经他讲解后,才知道其实生成静 ...

  10. [BZOJ3754]Tree之最小方差树

    3754: Tree之最小方差树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 402  Solved: 152[Submit][Status][Di ...