[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名称并不能得到匹配的类型。

102249182623800.png

导致上述这个问题的原因我们在上面已经分析过了:默认注册的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方法执行的结果。

102249196531043.png
作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
2月前
|
编解码 前端开发 JavaScript
.NET_web前端框架_layui_栅格布局
【8月更文挑战第27天】
38 4
|
13天前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
13天前
|
开发框架 .NET 中间件
ASP.NET Core Web 开发浅谈
本文介绍ASP.NET Core,一个轻量级、开源的跨平台框架,专为构建高性能Web应用设计。通过简单步骤,你将学会创建首个Web应用。文章还深入探讨了路由配置、依赖注入及安全性配置等常见问题,并提供了实用示例代码以助于理解与避免错误,帮助开发者更好地掌握ASP.NET Core的核心概念。
34 3
|
18天前
|
开发框架 前端开发 .NET
VB.NET中如何利用ASP.NET进行Web开发
在VB.NET中利用ASP.NET进行Web开发是一个常见的做法,特别是在需要构建动态、交互式Web应用程序时。ASP.NET是一个由微软开发的开源Web应用程序框架,它允许开发者使用多种编程语言(包括VB.NET)来创建Web应用程序。
43 5
|
1月前
|
前端开发 JavaScript
【前端web入门第三天】01 css定义和引入方式 四种标签选择器
本文档详细介绍了CSS的基础知识及其应用。内容涵盖了CSS的定义、CSS在HTML中的引入方式,包括内部样式表、外部样式表及行内样式表的使用场景与方法。此外,还深入解析了不同种类的选择器:标签选择器、类选择器、ID选择器以及通配符选择器的功能与应用场景,并提供了实例帮助理解。最后,通过具体的新属性示例,指导如何使用这些选择器来实现基本的盒子绘制。适合初学者系统学习CSS。
39 15
|
2月前
|
人工智能 前端开发 开发工具
NET在企业级应用、Web开发、移动应用、云服务及游戏领域的创新实践
.NET技术自2000年推出以来,在软件开发领域扮演着关键角色。本文从核心优势出发,探讨其统一多语言平台、强大工具集、跨平台能力及丰富生态系统的价值;随后介绍.NET在企业级应用、Web开发、移动应用、云服务及游戏领域的创新实践;最后分析性能优化、容器化、AI集成等方面的挑战与机遇,展望.NET技术的未来发展与潜力。
51 2
|
2月前
|
Java Spring 供应链
Spring 框架事件发布与监听机制,如魔法风暴席卷软件世界,开启奇幻编程之旅!
【8月更文挑战第31天】《Spring框架中的事件发布与监听机制》介绍了Spring中如何利用事件发布与监听机制实现组件间的高效协作。这一机制像城市中的广播系统,事件发布者发送消息,监听器接收并响应。通过简单的示例代码,文章详细讲解了如何定义事件类、创建事件发布者与监听器,并确保组件间松散耦合,提升系统的可维护性和扩展性。掌握这一机制,如同拥有一把开启高效软件开发大门的钥匙。
39 0
|
2月前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
36 0
|
2月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
37 0
|
2月前
|
存储 开发框架 .NET
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间
ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间

热门文章

最新文章