[ASP.NET Web API]如何Host定义在独立程序集中的Controller

通过《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的更多相关文章
- 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 ...
- How ASP.NET Web API 2.0 Works?[持续更新中…]
一.概述 RESTful Web API [Web标准篇]RESTful Web API [设计篇] 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 二.路由 ...
- 【ASP.NET Web API教程】3.2 通过.NET客户端调用Web API(C#)
原文:[ASP.NET Web API教程]3.2 通过.NET客户端调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...
- 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)
原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...
- Self-Host c#学习笔记之Application.DoEvents应用 不用IIS也能執行ASP.NET Web API
Self-Host 寄宿Web API 不一定需要IIS 的支持,我们可以采用Self Host 的方式使用任意类型的应用程序(控制台.Windows Forms 应用.WPF 应用甚至是Wind ...
- 打造属于自己的支持版本迭代的Asp.Net Web Api Route
在目前的主流架构中,我们越来越多的看到web Api的存在,小巧,灵活,基于Http协议,使它在越来越多的微服务项目或者移动项目充当很好的service endpoint. 问题 以Asp.Net W ...
- ASP.NET Web API路由系统:Web Host下的URL路由
ASP.NET Web API提供了一个独立于执行环境的抽象化的HTTP请求处理管道,而ASP.NET Web API自身的路由系统也不依赖于ASP.NET路由系统,所以它可以采用不同的寄宿方式运行于 ...
- Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?
构成ASP.NET Web API核心框架的消息处理管道既不关心请求消息来源于何处,也不需要考虑响应消息归于何方.当我们采用Web Host模式将一个ASP.NET应用作为目标Web API的宿主时, ...
- ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇]
ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇] 我们知道ASP.NET Web API借助于HttpSelfHostServer以Self Host模式寄宿于当 ...
随机推荐
- linux下安装apache与php;Apache+PHP+MySQL配置攻略
1.apache 在如下页面下载apache的for Linux 的源码包 http://www.apache.org/dist/httpd/; 存至/home/xx目录,xx是自建文件 ...
- Beginning Scala study note(7) Trait
A trait provides code reusability in Scala by encapsulating method and state and then offing possibi ...
- 关于安装Apache之后,解析PHP的配置
需要配置四个地方 LoadModule php5_module modules/libphp5.soServerName localhost:80DirectoryIndex index.phpAdd ...
- NPM 使用淘宝镜像
--registry https://registry.npm.taobao.org
- hack
1.Firefox @-moz-document url-prefix() { .selector { property: value; } }上面是仅仅被Firefox浏览器识别的写法,具体如: @ ...
- jQuery技巧
回到顶部按钮 图片预加载 判断图片是否加载完 自动修补破损图像 Hover切换class类 禁用输入 停止正在加载的链接 toggle fade/slide 简单的手风琴 使两个DIV同等高度 在浏览 ...
- 【ORACLE】MD5加密
今天乌干达充值卡入库时,发现有资源已经存在的异常, 异常原因经过核实是由于卡资源密码在库中已经存在, 为进一步查找存在的原因, 因此需要对导入文件密码的MD5 加密, 通过MD5加密后的字符串 ...
- 踏上Salesforce的学习之路(一)
相信通过前面的学习,大家已经拥有了一个属于自己的Salesforce开发者账号,下面,我们将用这个账号正式踏上Salesforce的学习之路. 首先,点击网址:https://developer.sa ...
- java实现面向对象和javaScript基于对象的区别&java垃圾回收机制和其他编程语言的比较
java javaScript javaGC和C语言内存分配和内存释放
- 设置Flush刷新模式setFlushMode()
参考 http://blog.csdn.net/superdog007/article/details/38852399 FlushMode的枚举值: FlushMode.ALWAYS:任务一条SQL ...