在前面對管道、路由有了基礎的了解過后,本篇將帶大家一起學習一下在ASP.NET Web API中控制器的創(chuàng)建過程,這過程分為幾個部分下面的內(nèi)容會為大家講解第一個部分,也是ASP.NET Web API框架跟ASP.NET MVC框架實現(xiàn)上存在不同的一部分。
目前成都創(chuàng)新互聯(lián)已為上千家的企業(yè)提供了網(wǎng)站建設、域名、網(wǎng)頁空間、網(wǎng)站托管、服務器租用、企業(yè)網(wǎng)站設計、晉源網(wǎng)站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。ASP.NET Web API 控制器創(chuàng)建過程(一)
ASP.NET Web API 控制器創(chuàng)建過程(二)
未完待續(xù)
在項目運用中,我們大多數(shù)會把控制器部分從主程序抽離出來放置單獨的項目中,這種情況下在使用ASP.NET MVC框架的項目環(huán)境中是不會有什么問題的,因為MVC框架在創(chuàng)建控制器的時候會加載當前主程序引用的所有程序集并且按照執(zhí)行的搜索規(guī)則(公共類型、實現(xiàn)IController的)搜索出控制器類型并且緩存到xml文件中。而這種方式如果在使用了默認的ASP.NET Web API框架環(huán)境下就會有一點點的問題,這里就涉及到了Web API框架的控制器創(chuàng)建過程中的知識。來看一下簡單的示例。
(示例還是《ASP.NET Web API 開篇介紹示例》中的示例,不過做了略微的修改,符合上述的情況。)
我們還是在SelfHost環(huán)境下做示例,來看SelfHost環(huán)境下服務端配置:
示例代碼1-1
classProgram { staticvoidMain(string[] args) { HttpSelfHostConfigurationselfHostConfiguration= newHttpSelfHostConfiguration("http://localhost/selfhost"); using (HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration)) { selfHostServer.Configuration.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id=RouteParameter.Optional }); selfHostServer.OpenAsync(); Console.WriteLine("服務器端服務監(jiān)聽已開啟"); Console.Read(); } } }
代碼1-1就是引用《ASP.NET Web API 開篇介紹示例》中的示例,在示例SelfHost項目中定義了API控制器,在這里我們需要把它注釋掉,并且創(chuàng)建新的類庫項目命名為WebAPIController,并且引用System.Web.Http.dll程序集和Common程序集,然后再定義個API控制器,也就是把原先在SelfHost項目中的控制器移動到新建的類庫項目中。
示例代碼1-2
usingSystem.Web.Http; usingCommon; namespaceWebAPIController { publicclassProductController : ApiController { privatestaticList<Product>products; staticProductController() { products=newList<Product>(); products.AddRange( newProduct[] { newProduct(){ ProductID="001", ProductName="牙刷",ProductCategory="洗漱用品"}, newProduct(){ ProductID="002", ProductName="《.NET框架設計—大型企業(yè)級應用框架設計藝術》", ProductCategory="書籍"} }); } publicIEnumerable<Product>Get(stringid=null) { returnfromproductinproductswhereproduct.ProductID==id||string.IsNullOrEmpty(id) selectproduct; } publicvoidDelete(stringid) { products.Remove(products.First(product=>product.ProductID==id)); } publicvoidPost(Productproduct) { products.Add(product); } publicvoidPut(Productproduct) { Delete(product.ProductID); Post(product); } } }
這個時候還要記得把SelfHost項目添加WebAPIController項目的引用,要保持跟MVC項目的環(huán)境一樣,然后我們在運行SelfHost項目,等待監(jiān)聽開啟過后再使用瀏覽器請求服務會發(fā)現(xiàn)如下圖所示的結(jié)果。
圖1
看到圖1中的顯示問題了吧,未找到匹配的控制器類型。如果是MVC項目則不會有這樣的問題,那么問題出在哪呢?實現(xiàn)方式的差異,下面就為大家來解釋一下。
在上一篇中我們最后的示意圖里可以清晰的看到ASP.NET Web API框架中的管道模型最后是通過HttpControllerDispatcher類型的對象來“生成”的APIController。我們現(xiàn)在就來看一下HttpControllerDispatcher類型的定義。
示例代碼1-3
publicclassHttpControllerDispatcher : HttpMessageHandler { //Fields privatereadonlyHttpConfiguration_configuration; privateIHttpControllerSelector_controllerSelector; //Methods publicHttpControllerDispatcher(HttpConfigurationconfiguration); privatestaticHttpResponseMessageHandleException(HttpRequestMessagerequest, Exceptionexception); protectedoverrideTask<HttpResponseMessage>SendAsync(HttpRequestMessagerequest, CancellationTokencancellationToken); privateTask<HttpResponseMessage>SendAsyncInternal(HttpRequestMessagerequest, CancellationTokencancellationToken); //Properties publicHttpConfigurationConfiguration { get; } privateIHttpControllerSelectorControllerSelector { get; } }
從示例代碼1-3中可以看到HttpControllerDispatcher類型繼承自HttpMessageHandler類型,由此可見,通過前面《ASP.NETWeb API 管道模型》篇幅的知識我們了解到在ASP.NET Web API管道的最后一個消息處理程序?qū)嶋H是HttpControllerDispatcher類型,在Web API框架調(diào)用HttpControllerDispatcher類型的SendAsync()方法時實際是調(diào)用了HttpControllerDispatcher類型的一個私有方法SendAsyncInternal(),而APIController就是在這個私有方法中生成的,當然不是由這個私有方法來生成它的。下面我們就來看一下SendAsyncInternal()的基礎實現(xiàn)。
示例代碼1-4
privateTask<HttpResponseMessage>SendAsyncInternal(HttpRequestMessagerequest, CancellationTokencancellationToken) { IHttpRouteDatarouteData=request.GetRouteData(); HttpControllerDescriptordescriptor=this.ControllerSelector.SelectController(request); IHttpControllercontroller=descriptor.CreateController(request); }
代碼1-4很清晰的說明了APIController的生成過程,這只是片面的,這里的代碼并不是全部,我們現(xiàn)在只需關注APIController的生成過程,暫時不去關心它的執(zhí)行過程。
首先由HttpControllerDispatcher類型中的一個類型為IHttpControllerSelector的屬性ControllerSelector(實則是DefaultHttpControllerSelector類型)根據(jù)HttpRequestMessage參數(shù)類型來調(diào)用IHttpControllerSelector類型里的SelectController()方法,由此獲取到HttpControllerDescriptor類型的變量descriptor,然后由變量descriptor調(diào)用它的CreateController()方法來創(chuàng)建的控制器。
說到這里看似一個簡單的過程里面蘊含的知識還是有一點的,我們首先來看ControllerSelector屬性:
示例代碼1-5
privateIHttpControllerSelectorControllerSelector { get { if (this._controllerSelector==null) { this._controllerSelector=this._configuration.Services.GetHttpControllerSelector(); } returnthis._controllerSelector; } }
這里我們可以看到是由HttpConfiguration類型的變量_configuration中的Servieces(服務容器)來獲取的IHttpControllerSelector類型的對象。那我們獲取到的究竟實例是什么類型的?
到這里先暫停,大家先不用記住上面的一堆廢話中的內(nèi)容,現(xiàn)在我們來看一下HttpConfiguration這個類型,我們現(xiàn)在只看HttpConfiguration類型,忘掉上面的一切。
示例代碼1-6
publicclassHttpConfiguration : IDisposable { //Fields privateIDependencyResolver_dependencyResolver; //Methods publicHttpConfiguration(); publicHttpConfiguration(HttpRouteCollectionroutes); privateHttpConfiguration(HttpConfigurationconfiguration, HttpControllerSettingssettings); publicIDependencyResolverDependencyResolver { get; set; } publicServicesContainerServices { get; internalset; } }
在這里我們看到有字段、構(gòu)造函數(shù)和屬性,對于_dependencyResolver字段和對應的屬性我們下面會有講到這里就不說了。這里主要就是說明一下ServicesContainer 類型的Services屬性。對于ServicesContainer類型沒用的朋友可能不太清楚,實際上可以把ServicesContainer類型想象成一個IoC容器,就和IDependencyResolver類型的作用是一樣的,在前面的《C#編程模式之擴展命令》一文中有涉及到ServicesContainer類型的使用,感興趣的朋友可以去看看。
回到主題,在構(gòu)造函數(shù)執(zhí)行時ServicesContainer類型實例實際是由它的子類DefaultServices類型實例化而來的。而在DefaultServices類型實例化的時候它的構(gòu)造函數(shù)中會將一些服務和具體實現(xiàn)的類型添加到緩存里。
圖2
在圖2中我們現(xiàn)在只需關注第二個紅框中的IHttpControllerSelector對應的具體服務類型是DefaultHttpControllerSelector類型。
現(xiàn)在我們再來看代碼1-5中的GetHttpControllerSelector()方法,它是有ServicesExtensions擴展方法類型來實現(xiàn)的。
好了現(xiàn)在大家的思緒可以回到代碼1-4中,現(xiàn)在我們知道是由DefaultHttpControllerSelector類型的SelectController()方法來生成HttpControllerDescriptor類型的。暫時不用管HttpControllerDescriptor類型,我們先來看SelectController()方法中的實現(xiàn)細節(jié)。
示例代碼1-7
publicvirtualHttpControllerDescriptorSelectController(HttpRequestMessagerequest) { HttpControllerDescriptordescriptor; stringcontrollerName=this.GetControllerName(request); if (this._controllerInfoCache.Value.TryGetValue(controllerName, outdescriptor)) { returndescriptor; } }
這里可以看到controllerName是由路由數(shù)據(jù)對象RouteData來獲取的,然后由_controllerInfoCache變量根據(jù)控制器名稱獲取到HttpControllerDescriptor實例,這里HttpControllerDescriptor實例我們暫且不管,后面的篇幅會有講到。
重點是我們看一下_controllerInfoCache變量中的值是怎么來的?是從HttpControllerTypeCache類型的Cache屬性值而來。而Cache的值則是根據(jù)HttpControllerTypeCache類型的中的InitializeCache()方法得來的,我們看下實現(xiàn)。
實例代碼1-8
privateDictionary<string, ILookup<string, Type>>InitializeCache() { IAssembliesResolverassembliesResolver=this._configuration.Services.GetAssembliesResolver(); returnthis._configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(assembliesResolver).GroupBy<Type, string>(t=>t.Name.Substring(0, t.Name.Length-DefaultHttpControllerSelector.ControllerSuffix.Length), StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string, Type>, string, ILookup<string, Type>>(g=>g.Key, g=>g.ToLookup<Type, string>(t=> (t.Namespace??string.Empty), StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); }
還記得文章的主題嗎?回想一下,在本篇幅的內(nèi)容只涉及到代碼1-8的第一句代碼,后面篇幅會繼續(xù)往下講解,我們就來看一下第一句代碼。
第一句是獲取IAssembliesResolver類型的實例assembliesResolver,而之對應的服務則是在圖2中顯示出來了,就是DefaultAssembliesResolver類型,DefaultAssembliesResolver類型控制著框架搜尋的程序集范圍,罪魁禍首在這。
示例代碼1-9
publicclassDefaultAssembliesResolver : IAssembliesResolver { //Methods publicvirtualICollection<Assembly>GetAssemblies() { returnAppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>(); } }
看到這里大家應該明白了,在SelfHost環(huán)境下的服務端啟動之后就沒有加載我們所想要的WebAPIController程序集,所以才會有圖1所示的問題。這個我們可以來查看一下。
將1-1代碼修改如示例代碼1-10.
代碼1-10
classProgram { staticvoidMain(string[] args) { HttpSelfHostConfigurationselfHostConfiguration= newHttpSelfHostConfiguration("http://localhost/selfhost"); using (HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration)) { selfHostServer.Configuration.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id=RouteParameter.Optional }); selfHostServer.OpenAsync(); foreach (AssemblyassemblyinAppDomain.CurrentDomain.GetAssemblies()) { Console.WriteLine(assembly.FullName.Substring(0,assembly.FullName.IndexOf("Version"))); } Console.WriteLine("服務器端服務監(jiān)聽已開啟"); Console.Read(); } } }
這個時候我們啟動SelfHost項目,就可以查看到到底有沒有我們想要的程序集,如圖3。
圖3
可以看一下,根本沒有我們所需的,那怎么樣才能正常的運行起來如一開始所說的那樣呢?
示例代碼1-11
usingSystem.Web.Http.Dispatcher; usingSystem.Reflection; namespaceSelfHost.CustomAssembliesResolver { publicclassLoadSpecifiedAssembliesResolver : IAssembliesResolver { publicICollection<Assembly>GetAssemblies() { AppDomain.CurrentDomain.Load("WebAPIController"); returnAppDomain.CurrentDomain.GetAssemblies(); } } }
我們通過自定義AssembliesResolver來加載我們指定的程序集,并且最后要把我們實現(xiàn)的替換到Web API框架中,如下代碼。
示例代碼1-12
classProgram { staticvoidMain(string[] args) { HttpSelfHostConfigurationselfHostConfiguration= newHttpSelfHostConfiguration("http://localhost/selfhost"); using (HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration)) { selfHostServer.Configuration.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id=RouteParameter.Optional }); selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver), newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver()); selfHostServer.OpenAsync(); Console.WriteLine("服務器端服務監(jiān)聽已開啟"); Console.Read(); } } }
這個時候我們再運行SelfHost項目,并且使用瀏覽器來請求,最后結(jié)果如圖4.
圖4
在WebHost環(huán)境中則不會發(fā)生上述所描述的問題,為什么?
示例代碼1-13
publicclassGlobal : System.Web.HttpApplication { protectedvoidApplication_Start(objectsender, EventArgse) { GlobalConfiguration.Configuration.Routes.MapHttpRoute( "DefaultAPI", "api/{controller}/{id}", new { controller="product",id=RouteParameter.Optional }); } }
代碼1-13表示著WebHost環(huán)境下的路由注冊,看起來比SelfHost環(huán)境要簡便的多。因為“簡便”封裝在GlobalConfiguration類型中了,前面的文章也對GlobalConfiguration類型做過介紹,不過并沒有把重點放到IAssembliesResolver服務上?,F(xiàn)在我們來看一下實現(xiàn)。
示例代碼1-14
privatestaticLazy<HttpConfiguration>_configuration=newLazy<HttpConfiguration>(delegate { HttpConfigurationconfiguration=newHttpConfiguration(newHostedHttpRouteCollection(RouteTable.Routes)); configuration.Services.Replace(typeof(IAssembliesResolver), newWebHostAssembliesResolver()); configuration.Services.Replace(typeof(IHttpControllerTypeResolver), newWebHostHttpControllerTypeResolver()); configuration.Services.Replace(typeof(IHostBufferPolicySelector), newWebHostBufferPolicySelector()); returnconfiguration; });
我們可以清楚的看到DefaultAssembliesResolver類型被替換成了WebHostAssembliesResolver類型。為什么WebHost不會有這樣的問題就都在WebHostAssembliesResolver類型當中了。
示例代碼1-15
internalsealedclassWebHostAssembliesResolver : IAssembliesResolver { //Methods ICollection<Assembly>IAssembliesResolver.GetAssemblies() { returnBuildManager.GetReferencedAssemblies().OfType<Assembly>().ToList<Assembly>(); } }
把WebHost環(huán)境中的結(jié)構(gòu)修改的和SelfHost環(huán)境中的一樣,然后請求則會發(fā)現(xiàn)不會遇到什么問題。
圖5
下一篇將會講解一下APIController的生成過程,也就是代碼1-8的第二句代碼部分。
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
本文題目:ASP.NETWebAPI控制器創(chuàng)建過程(一)-創(chuàng)新互聯(lián)
標題網(wǎng)址:http://www.rwnh.cn/article26/cschjg.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、網(wǎng)站營銷、用戶體驗、營銷型網(wǎng)站建設、外貿(mào)建站、App設計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容