下面就一些web api的一些基礎內容進行闡述,然后就web api宿主承載中的實際業務問題進行解決
HttpController
HttpController的激活是由處於消息處理管道尾端的HttpRoutingDispatcher來完成的,具體來說是HttpRoutingDispatcher利用HttpControllerDispatcher實現了針對目標HttpController的激活和執行。激活目標HttpController的前提是能夠正確解析出HttpController的真實類型,而類型解析需要針對加載的程序集,所以我們需要先來了解一個用於解析程序集的對象AssembliesResolver。在ASP.NET Web API的HttpController激活系統中,AssembliesResolver為目標HttpController的類型解析提供候選的程序集。換句話說,候選HttpController類型的選擇范圍僅限於定義在通過AssembliesResolver提供的程序集中的所有實現了IHttpController接口的類型
AssembliesResolver
所有的AssembliesResolver均實現了IAssembliesResolver接口。根據程序反射得到如下代碼片段,根據代碼片段得知,IAssembliesResolver提供的是程序集列表
public interface IAssembliesResolver { ICollection<Assembly> GetAssemblies(); }
DefaultAssembliesResolver
默認的AssembliesResolver為DefaultAssembliesResolver,根據以下代碼片段得知,默認返回的是當前應用程序域的程序集
public class DefaultAssembliesResolver : IAssembliesResolver { public virtual ICollection<Assembly> GetAssemblies() { return AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>(); } }
ServicesContainer
默認的AssembliesResolver就是通過ServicesContainer類型確定
Web Api的請求相當於一個管道,類似於流水線作業,每個環節都會注冊自己的實現類組件來完成自己的工作。這些組件都會實現自己特定的接口,當然在預置的組件無法滿足我們的業務需求時,可以自己來實現組件代碼,並進行注冊,ServicesContainer其實可以簡單的理解為這些組件的一個IOC容器
上面所介紹的就代表着,如果采用selfHost承載web api,而且web api與宿主程序不在同一程序集中的情況,如果在用上篇的代碼,
將會發現報以下錯誤 No type was found that matches the controller named 'User'.
上面所說的情況,分兩種,第一種,單個api程序集,多個api程序集
1.如果api程序集全部寫在一個單獨的程序集中的情況
1.1 直接通過Assembly加載外部程序集
static void Main(string[] args) { // 如果 API 處於外部程序集,可通過以下代碼加載 CM.API為其他程序集 //Assembly.Load("CM.API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); //加載外部程序集 string baseAddress = "http://localhost:9000/"; // 啟動 OWIN host WebApp.Start<Startup>(url: baseAddress); Console.WriteLine("程序已啟動,按任意鍵退出"); Console.ReadLine(); }
宿主項目還是需要引用CM.API程序集
1.2通過自定義AssembliesResolber來進行注冊
public class UserResolver: DefaultAssembliesResolver { public override ICollection<Assembly> GetAssemblies() { ICollection<Assembly> baseAssemblies = base.GetAssemblies(); List<Assembly> assemblies = new List<Assembly>(baseAssemblies); var assembly = AppDomain.CurrentDomain.BaseDirectory; string path = assembly + "\\" + "CM.API.dll"; var controllersAssembly = Assembly.LoadFrom(path); baseAssemblies.Add(controllersAssembly); return assemblies; } }
然后宿主啟動時進行注冊
static void Main(string[] args) { var config = new HttpSelfHostConfiguration("http://localhost:8083"); config.Routes.MapHttpRoute( "API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional }); //注冊UserResolver,加載程序集 config.Services.Replace(typeof(IAssembliesResolver), new UserResolver()); using (var server = new HttpSelfHostServer(config)) { server.OpenAsync().Wait(); Console.WriteLine("Press Enter to quit."); Console.ReadLine(); } }
2.當然在業務開發中如果API被分散開發到多個程序集中,這種情況當然根據項目框架區分,各有利弊
2.1 通過上述的UserServoler進行加載
public class UserResolver: DefaultAssembliesResolver { public override ICollection<Assembly> GetAssemblies() { ICollection<Assembly> baseAssemblies = base.GetAssemblies(); List<Assembly> assemblies = new List<Assembly>(baseAssemblies); var assembly = AppDomain.CurrentDomain.BaseDirectory; string path = assembly + "\\" + "CM.API.dll"; var controllersAssembly = Assembly.LoadFrom(path); baseAssemblies.Add(controllersAssembly); //加載多個程序集 不建議寫死 string path2 = assembly + "\\" + "CM.API2.dll"; var controllersAssembly2 = Assembly.LoadFrom(path2); baseAssemblies.Add(controllersAssembly2); return assemblies; } }
上述代碼只是展示加載的一種方式,只是得到程序集的方式需要去進行配置編碼,不建議直接寫死在代碼里
2.2 通過自定配置類,進行配置文件讀取加載
添加自定義配置類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Configuration; using System.Reflection; namespace CM.SelfHost { public class AssembliesLoad : ConfigurationSection { [ConfigurationProperty("", IsDefaultCollection = true)] public AssemblyElementCollection AssemblyNames { get { return (AssemblyElementCollection)this[""]; } } public static AssembliesLoad GetSection() { return ConfigurationManager.GetSection("AssembliesLoad") as AssembliesLoad; } } public class AssemblyElementCollection : ConfigurationElementCollection { protected override ConfigurationElement CreateNewElement() { return new AssemblyElement(); } protected override object GetElementKey(ConfigurationElement element) { AssemblyElement serviceTypeElement = (AssemblyElement)element; return serviceTypeElement.AssemblyName; } } public class AssemblyElement : ConfigurationElement { [ConfigurationProperty("assemblyName", IsRequired = true)] public string AssemblyName { get { return (string)this["assemblyName"]; } set { this["assemblyName"] = value; } } } }
添加配置文件,如下,加入ConfigSections節點以及AssembliesLoad節點
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <configSections> <section name="AssembliesLoad" type="CM.SelfHost.AssembliesLoad,CM.SelfHost"/> </configSections> <AssembliesLoad> <add assemblyName="CM.API.dll" /> </AssembliesLoad> </configuration>
添加AssemblyResolver類,並注冊
public class WebApiResolver: DefaultAssembliesResolver { public override ICollection<Assembly> GetAssemblies() { AssembliesLoad settings = AssembliesLoad.GetSection(); if (null != settings) { foreach (AssemblyElement element in settings.AssemblyNames) { AssemblyName assemblyName = AssemblyName.GetAssemblyName(element.AssemblyName); if (!AppDomain.CurrentDomain.GetAssemblies().Any(assembly => AssemblyName.ReferenceMatchesDefinition(assembly.GetName(), assemblyName))) { AppDomain.CurrentDomain.Load(assemblyName); } } } return base.GetAssemblies(); } }
注冊Resolver
static void Main(string[] args) { //Assembly.Load("CM.API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); //加載外部程序集 var config = new HttpSelfHostConfiguration("http://localhost:8083"); config.Routes.MapHttpRoute( "API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional }); config.Services.Replace(typeof(IAssembliesResolver), new UserResolver()); using (var server = new HttpSelfHostServer(config)) { server.OpenAsync().Wait(); Console.WriteLine("Press Enter to quit."); Console.ReadLine(); } }
運行會報錯
其他信息: 配置系統未能初始化
查閱一些說明之后發現,如果存在自定義配置ConfigSections節點,那么必須在第一個節點,修改app.confg如下
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="AssembliesLoad" type="CM.SelfHost.AssembliesLoad,CM.SelfHost"/> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <AssembliesLoad> <add assemblyName="CM.API.dll" /> </AssembliesLoad> </configuration>
至此,web api的承載方式就已經闡述完畢,說一下個人傾向,通過兩篇文章對承載的了解,我喜歡使用OwinselfHost承載,並采用自定義配置文件的方式加載外部api程序集,Owin有跨平台功能,但沒有去進行驗證,大家有興趣可以去驗證下