標籤:網站目錄 auth sax sep 技術 接受 路徑 pager 還需
上篇部落格【寫自己的ASP.NET MVC架構(上)】 我給大家介紹我的MVC架構對於Ajax的支援與實現原理。今天的部落格將介紹我的MVC架構對UI部分的支援。
注意:由於這篇部落格是基於前篇部落格的,因此有些已說過的內容將會直接跳過,也不會給出提示。
所以,如果要想理解這篇部落格,那麼閱讀上篇部落格【寫自己的ASP.NET MVC架構(上)】則是必要的。
回到頂部MyMVC的特點
在開發MyMVC的過程中,我吸取了一些ASP.NET WebForm的使用經驗,也參考了ASP.NET MVC,也接受了Martin Fowler對於MVC思想的總結。 在設計過程中,我只實現了一些必要的功能,而且沒有引入其它的類庫與組件,因此,它非常簡單,且容易使用。
我們可以這樣理解MyMVC:它是一個簡單,容易使用,且符合MVC思想的架構。
在MyMVC架構中,View仍然採用了WebForm中的Page,畢竟Page已經使用了十年,能經得起時間的檢驗,它仍然是我們可信賴的技術。 另一方面,Page也是ASP.NET中預設的HTML輸出技術,使用它會比較方便。
MyMVC與微軟的ASP.NET MVC不同的是:
1. 不依賴於URL路由群組件。
2. 不提供任何HtmlHelper
3. Controller只是一個Action的容器,沒有基類的要求。
4. Action處理的請求不區分POST, GET
5. URL可以直接對應一個網站目錄中的aspx頁面(View)。
6. View的使用是使用路徑來指定,與Controller,Action的名字無關。
說明:URL雖然可以與網站中的頁面對應,但這種對應並不是必須的,也可以不對應。
而且本質上與WebFrom中的頁面執行過程並不相同。
反映了在MyMVC中,一個頁面請求的執行過程:
回到頂部介紹樣本項目
為了讓大家對MyMVC有興趣,也為了檢驗MyMVC的設計,我在開發MyMVC的過程,還專門開發一個基於MyMVC的ASP.NET網站樣本項目。 網站提供了三種顯示風格(也就是三種View),下面以“客戶管理”頁面為例來展示三種View的不同:
風格1
View對應的代碼如下:
風格2
View對應的代碼如下:
風格3
View對應的代碼如下:
這是三種截然不同的風格,在服務端的代碼也是完全不同的。
其中第二種風格,是採用了我上篇部落格中總結的【純AJAX網站】的風格來開發,因此在服務端頁面的開發過程中,最為簡單,它需要輸出的HTML最少,UI部分由用戶端的JS來實現。
對於第一種和第三種風格,它們的HTML結構是不同的,頁面所能完成的功能也是不同的, 除此之外,它們應該是比較類似的,都是從下面這個泛型型別繼承而來:
Inherits="MyPageView<CustomersPageModel>"
從泛型型別繼承的好處是:我可以在設計頁面時,對於涉及Model的訪問,都會有智能提示。比如:
由於有智能提示的支援,可以提高開發效率,並可以避免一些低級的拼字錯誤。
雖然前面我們可以從圖片中看到訪問【同一個URL地址】出現【三個不同的頁面】,但它們背後的Controller卻是同一個:
通過上面代碼可以看到我用了4個http://www.mamicode.com/info-detail-1727250.html,這意味著其實我可以使用4種不同的URL都能訪問到這三個頁面, 而且每一個URL都會根據目前使用者所選擇的風格,呈現對應的頁面。
事實上,我還可以為這個Action指定更多的http://www.mamicode.com/info-detail-1727250.html,讓它可以處理更多的URL。關於http://www.mamicode.com/info-detail-1727250.html的使用與設計目的,請繼續往下閱讀。
回到頂部關於URL路由
隨著 .net framewrok 3.5 的問世,微軟發布了一個【ASP.NET 路由】組件,它的出現給當時的URL最佳化方法提供了另外一種選擇, 不僅如此,它還提供了一些URL重寫組件沒有的功能:產生URL 。
隨著AP.NET MVC的出現,【ASP.NET 路由】成為此架構的直接相依元件,我們很難有其它的選擇, 而且,想不用都不行。
有趣的是:【ASP.NET 路由】這個後生小子的出現,並沒有很好地遵守ASP.NET制定的一些規則, 其中最為明顯的是:它跳過了【處理器的映射】階段,導致ASP.NET MVC在支援Session時,很為難。 直到最後ASP.NET 4.0,微軟修改了Session的部分實現方式,這樣ASP.NET MVC才能最終藉此機會解決Session的完整支援問題。
ASP.NET 路由雖然可以產生URL,但它引入了RouteData的概念,要想支援它,需要在架構層面上做許多基礎工作。
而且,我認為:
1. 並不是每個網站都需要這種技術,對於不需要URL最佳化的網站來說,URL路由的使用只是白白地浪費效能。
2. 另一方面,即使需要URL最佳化,我們還有眾多的URL重寫組件可供選擇,這樣可以不用改變現在構架。
因此,MyMVC雖然不支援URL路由,但並不表示不能實現URL最佳化。
在MVC思想中,Controller應該是處理請求的地方,也是最先啟動並執行部分。 然而在傳統的WebForm編程模型中,aspx頁面負責處理請求。 因此,必須採取一種方式讓最先處理請求的地方從aspx頁面中轉移,並能提前執行。
而且,將代碼從頁面移出還有另外二個好處:
1. 被移出的代碼肯定是與UI部分無關的,因此,會比較容易測試。
2. 代碼與UI的分享也意味著:可以根據運行條件,有選擇地將結果交給不同的View來呈現。
考慮到Action可以選擇將結果交給不同的View來呈現,而Session也需要支援的問題, 最終我決定,在架構內部使用一個專門的HttpHandler來執行使用者的Action,根據Action所要求的Session支援模式, HttpHandlerFactory建立不同的HttpHandler來支援。由於需要使用HttpHandlerFactory,所以必須在web.config中註冊。
回到頂部配置MyMVC架構
MyMVC在使用時,需要在web.config中簡單的配置:
<httpHandlers> <add path="*.aspx" verb="*" type="MyMVC.MvcPageHandlerFactory, MyMVC" validate="true"/></httpHandlers>
如果使用IIS7,則參考以下配置:
我們可以把MvcPageHandlerFactory理解成MyMVC在ASP.NET管線的入口。
注意:
1. 上面的配置代碼中,選擇aspx這個副檔名並不是必須的,您也可以選擇喜歡的副檔名。
2. 如果不喜歡副檔名的映射,可以使用HttpModule,MyMVC中提供的方法也能替代這個過程。
回到頂部映射處理器(入口)
在web.config中註冊MvcPageHandlerFactory後,所有合格請求將會進入MvcPageHandlerFactory。
我們來看一下MvcPageHandlerFactory的實現代碼:
從代碼中可以看到,MyMVC首先會根據當前的請求地址尋找有沒有一個Action可以處理它,如果沒有,則採用ASP.NET預設的方式來處理。 因此,把【*.aspx】交給MvcPageHandlerFactory是不會有問題的。
說明:建立一個空殼類型AspnetPageHandlerFactory的原因是:不能直接調用PageHandlerFactory的建構函式。
回到頂部內部初始化
MyMVC在第一次處理請求時,要做一個初始化的過程,這個過程是由MvcPageHandlerFactory中的一個調用引發的:
// 嘗試根據請求路徑擷取ActionInvokeInfo vkInfo = ReflectionHelper.GetPageActionInvokeInfo(virtualPath);
ReflectionHelper有個靜態建構函式,雖然上次我已貼出它的代碼,但那隻是部分代碼,以下才是完整的初始化代碼:
從以上代碼可以看出,在初始化時,MyMVC載入了全部的PageAction ,而AjaxAction卻沒有採用這種方式來實現,為什麼呢? 請繼續閱讀。
回到頂部從URL到Action的映射過程
前面我們看到了MyMVC的初始化過程,其實是在ReflectionHelper的建構函式中完成的。 在這個初始化之後,MvcPageHandlerFactory調用ReflectionHelper.GetPageActionInvokeInfo(virtualPath)便可以得到要調用的Action的具體描述。 我稱這個過程為:從URL到Action的映射。
GetPageActionInvokeInfo方法的實現代碼如下:
在介紹這個映射過程之前,讓我們再來回顧一下Action的聲明代碼:
通過ReflectionHelper建構函式中所完成的初始化過程,每個Action的描述會根據http://www.mamicode.com/info-detail-1727250.html的數量而產生多個字典條目, 因此,在GetPageActionInvokeInfo的實現過程中,也只是簡單的尋找了這個字典而已,就可以得到所需要的調用資訊,從面完成映射的過程。 整個過程可以用以形來表示:
在上面的樣本中,我使用了"/mvc/Customers"這種URL,顯然它並不符合我在web.config中為MvcPageHandlerFactory註冊時所指定的URL模式要求。 那麼,又該如何處理呢?
雖然這種URL雖然沒有副檔名,但我仍然可以通過配置httpHandler的方式來解決,下面的配置就是我們需要的:
在介紹MvcPageHandlerFactory時,MyMVC提供了另一個方法TryGetHandler供外部使用。 因此,在樣本網站中,我還可以在Global.asax中調用這個方法來解決前面的那個問題:
對於切換HttpHandler的操作,我有以下建議:
1. 盡量放在HttpModule中去實現。因為可以通過修改配置來切換規則(啟用或者禁止),所以會比較靈活。
2. 如果可以通過HttpHandler映射能實現的,盡量首選HttpHandler映射方式。原因:更快,更標準。
回到頂部PageUrl的設計思想
在前面的範例程式碼中,我為一個Action添加多個http://www.mamicode.com/info-detail-1727250.html,來標記這個Action可以處理多個URL, 因此,一個Action能處理哪些URL是通過指定http://www.mamicode.com/info-detail-1727250.html來實現的。
為什麼要叫【PageUrl】?
我想或許有些人會有這個疑問。
下面我就來回答這個問題,也可以讓大家瞭解我設計PageUrl的原因:
1. 我們請求一個URL通常是為了得到一個頁面顯示,因此可以認為一個URL最終可以表示成一個頁面。
2. 我也想過使用[Url]這種名稱,但感覺太短了,而且Ajax請求也有URL,那麼必須顯式地加以區分。
所以,我最終決定使用http://www.mamicode.com/info-detail-1727250.html這個名字。
在Ajax部分,我認為通常只需要完成擷取資料以及處理提交資料的功能就可以了。 因此,絕大多數情況下是不要需View的,而且,一個功能與一個URL對應,這樣還可以簡化問題。 所以,在Ajax部分,我提倡在URL中直接指出要調用哪個Controller中的哪個Action。
在Page部分,事實上也需要一個Action,本來也是可以繼續使用這種做法的, 不過,我並沒有這種做,理由如下:
1. 我們建立View其實也是建立Page,使用Page的路徑不是更好嗎?而且WebForm的粉絲或許會更喜歡。
2. 多URL的匹配功能。後面會有詳細說明。
由於以上種種原因,我將http://www.mamicode.com/info-detail-1727250.html設計成與[Action]是獨立關係,並且http://www.mamicode.com/info-detail-1727250.html可以多次指定的。
注意:
1. Url參數中指定的字串,可以對應一個aspx頁面。也可以不對應aspx頁面。
2. Url參數中,不要包含QueryString,否則根本不能匹配。
3. 如果您使用URL重寫組件,那麼此處應該是重寫後的路徑。
由於我在MvcPageHandlerFactory中使用ASP.NET架構傳入的virtualPath並不包含查詢參數, 因此,把它理解成頁面路徑也是非常合適的。
回到頂部多URL的匹配功能
或許有些人認為多URL匹配一個Action是沒有意義的,比如下面的這個Action會更符合常理:
是的,通常情況下,一個Action處理一個URL也是較為常見。
但仍然有二種情況需要這個功能。首先來看下面的樣本:
代碼所涉及的4個頁面在呈現時,由於並不需要資料,但為了能夠實現多樣式的支援,它們可以共用一個Action,因此這裡只是切換一個View的路徑而已。
理解上面那句話,可能還需要知道StyleHelper的實現代碼:
樣本網站的目錄結構如:
在樣本網站中,由於三種風格的截然不同,尤其是在功能與HTML結構上就完全不同,因此根本不可能通過CSS或者SKIN的方式來解決, 所以我為三種風格建立了三個目錄,分別存放相應的分頁檔。 最終根據使用者的選擇(Cookie)來決定使用哪個目錄下的頁面來呈現。
使用者佈建風格的JS代碼如下,
服務端的C#代碼如下:
說明:CookieHelper是設計成支援單元測試的,所以不要懷疑這裡的代碼不符合MVC,後面會專門談它。
所以,在這種情況下,多個URL映射到一個Action是有意義的。這是【多URL的匹配功能】的第一個用途。
回到頂部解決老的URL相容問題
在一個網站的成長過程中,一般會有重構的過程。在重構過程中,或許會刪除以前的某些頁面,或許調整URL格式。 然而,使用者也可能會收藏這個網站的連結,但由於頁面重構了,老的連結可能會因此而失效,造成404錯誤。 此時就要解決URL的相容問題。
在ASP.NET中,我們可以在web.config配置urlMappings節點來做這樣的映射轉換。 還有另一種方法是,建立一個HttpModule專門判斷是否在請求一些老的URL,如果是,則重新導向到新的頁面。 總之,不管使用哪種方法,都需要為每個傳入請求檢查URL是否是老格式的URL, 這個過程會根據一個列表來逐一檢查,不過,可惜的是:絕大部分請求可能都是新的URL格式, 而那些相容方案無疑會浪費很多的CPU資源。
在MyMVC中,可以簡單地處理這個問題,就像下面的這個樣本一樣:
這個“客戶管理”頁面可能經過了多次重構,沒關係,只要把各個版本的地址用http://www.mamicode.com/info-detail-1727250.html標識出來就可以了,完全不用前面所說的相容方案, 因此,在URL的相容處理上沒有任何負擔,也不會影響效能。
說明:http://www.mamicode.com/info-detail-1727250.html的順序並不重要,可以隨意調整。
回到頂部對身份認證的支援
MyMVC也支援一些基本的身份認證,可以通過在Action方法中添加[Authorize]修飾屬性來指示。
AuthorizeAttribute的實現代碼如下:
認證檢查發生在調用Action之前,代碼如下:
下面的範例程式碼示範了它的用法:
注意:
1. 如果一個Action沒有使用[Authorize],則表示允許任意使用者訪問(包括未登入使用者)。
2. [Authorize]對於AjaxAction仍然有效。
回到頂部View的設計方式
在MyMVC中,View採用了ASP.NET Page,不過,我並不建議使用CodeFile檔案。 不使用CodeFile檔案,我想這是很多喜歡WebForm的人不能接受的。 他們更願意在CodeFile檔案中擷取資料,綁定資料,響應事件,處理使用者的提交資料。 也正是由於這個原因,才會讓其它人認為WebForm是一種對單元測試極差的編程模型。
這裡我要表達一下我的觀點:代碼是否可支援單元測試,這其中最主要的原因還是開發人員自身造成的, 架構的選擇只是起到促進或是部分限制的作用。 就算讓一些人使用ASP.NET MVC,他們所編寫的代碼未必就能支援單元測試, 有些人實在太依賴於HttpContext.Current,甚至在ASP.NET MVC中還在寫這種代碼。
好吧,還是回到Page的設計這個話題上來。MyMVC所提倡的做法與ASP.NET MVC的做法類似, 那就是直接在Page中採用內聯的方式顯示資料,而不是在CodeFile中綁定資料。 許多人一看到ASP.NET MVC的這種內聯寫法,感覺又回到了ASP時代,認為是在倒退,其實這隻是表面現象。 表面的背後是:代碼遠離了UI。,也可以理解成:邏輯遠離了UI。 這也是正是ASP.NET MVC一直所提倡的:分離關注點。 在新的開發理念中,原來的Page分解成View和Controller,在實現它們時,只關注自身那一部分就可以了, 因此,如果單看Page時,可能是會有前面所說的那種感覺。 另一方面,由於代碼遠離了UI,或許可以有更多的機會重構它們,使它們的重用性更高。
下面還是來回顧一下MyMVC中Page的代碼:
此時,對於呈現所需的資料可以直接從Model對象中擷取,但要求在Page指令中指出Model的類型,這樣還可以有智能提示的優點。 如果頁面需要顯示資料,請務必從MyPageView<>繼承,它的實現代碼如下:
其實也就是一個簡單的類型,包含了Model這個屬性而已。 至於MyBasePage的實現代碼,我們可以忽略它,它是直接從System.Web.UI.Page繼承的。
再來一段使用者控制項的代碼:
基本上,與Page的開發方式差不多,只是基類換成了MyUserControlView<>而已。
在這裡我認為要補充一點的是:
與ASP.NET MVC不同,MyMVC不提供任何HtmlHelper。
我認為HtmlHelper與MVC思想完全沒有關係,因此不提供這些方法。
另一方面,很多人希望更好地控制HTML代碼,因此就更沒必要提供這些方法了。
如果您認為需要一些必要的HtmlHelper方法,那麼可以實現自己喜歡的HtmlHelper類庫。
最後我想說的是:頁面繼承泛型類,還需要一些額外的處理。比如下面的代碼:
Inherits="MyPageView<CustomersPageModel>"
要讓這種設定能夠通過編譯,需要在web.config中做如下配置:
<pages pageParserFilterType="MyMVC.ViewTypeParserFilter, MyMVC" >
ViewTypeParserFilter的實現代碼較長,我就不在此貼出了,可以從本文結尾處下載。
回到頂部Controller,Action的設計方式
在MyMVC中,Action分為二種:AjaxAction和PageAction。
PageAction與AjaxActioin在方法的定義上並沒有什麼差異,只要是個public方法就可以了。
不過,PageAction與AjaxAction不同點在於:
1. Controller的容器名稱不同,PageAction要求Controller的名字必須以Controller結尾。
2. 必須有一個有效http://www.mamicode.com/info-detail-1727250.html的修飾屬性指出可以處理的URL
3. Action的名字與URL無關,可以隨意取名。
在MyMVC中,2種Action還有另一特點是:不區分GET,POST 。
原因是:我喜歡用JQuery,用它實現用戶端的Ajax時,GET, POST,只是一個參數的差別而已。 另一方面,對於HTML表單來說,GET, POST也只是一個參數的差別,大部分表單也可以通過GET方式來提交,只要您願意。 所以,我想,既然用戶端可以這樣靈活地切換,服務端也就沒有必要再去做那樣限制。 或許有些人認為區分二者會更安全,但我認為它們對安全性基本上不構成影響。 反而,如果服務端忽略它們,只會讓用戶端更容易調用。
還有一種情況下可能需要區分二者:請求與提交是同一個地址。
這應該可以算得上是我在上篇總結的【以服務端為中心的網站】的開發方式。
事實上,在使用MyMVC的項目中,<form>標籤應該需要手寫,可能更多的時候會提交到另一個地址,
因為,我更建議使用Ajax方式提交資料。
所以,最終我決定:MyMVC的Action不區分GET, POST.
在設計MyMVC時,我一直沒有忘記將View和Controller的分離,而且對於Controller,只有名字上的約束, Action的約束也較少,因此,我們在實現Action時,完全可以把它們獨立到【類庫項目】中,
就像樣本項目這樣:
這樣做的好處是:測試Actioin會更容易。
此時網站可能只是一堆aspx,js, css檔案。我一直期待能將aspx也交給美工去維護,這樣設計但願能讓可能性更大一些。
回到頂部輸出HTML的方式
MyMVC提供二種方式在Action中返回HTML,分別是返回PageResult或者UcResult,表示需要呈現一個頁面或者一個使用者控制項。 當在Action返回這二種結果時,Action的部分就執行完畢了。 剩下的處理是在MyMVC架構中進行的,MyMVC架構會對這二種結果,以IActionResult介面的方式調用Ouput方法輸出結果給用戶端。
PageResult和UcResult的實現代碼如下:
這二個類型的使用方式是一樣的,都需要提供二個參數,第一個參數表示頁面或者使用者控制項的存放路徑,第二個參數表示給頁面或者使用者控制項所需的顯示資料。 比如下面這個樣本:
設計這二類結果,我的本意是:
1. UcResult給Ajax請求使用,因為有可能會要求服務端輸出一段HTML
2. PageResult用於整頁面的響應。
在MyMVC中,執行頁面或者使用者控制項,需要指出頁面或者使用者控制項的路徑,而不是採用什麼約定關係。
我認為約定會造成名字耦合,約定也會影響限制靈活,因此,必須明確指定(允許為null)。
PageResult多用於PageAction,而PageAction又有http://www.mamicode.com/info-detail-1727250.html來指示可以處理哪些URL,雖然一個PageAction可以處理多個URL, 但通常情況下,還是以一個PageAction處理一個URL的情況居多。此時,MyMVC允許在返回PageResult時, 第一個參數可以設定為null,表示使用當前請求地址。 如果此時當前請求地址有一個aspx頁面與之對應,自然就會方便很多。 可以參考下面的樣本:
在MyMVC架構中,PageResult最終會調用PageExecutor.Render()來擷取頁面的產生代碼,具體過程如下:
UcResult則會調用UcExecutor.Render()產生使用者控制項的輸出代碼,具體過程如下:
回到頂部HTML分塊輸出
注意哦,前面介紹的2個Render方法的可見度都是public,這樣設計的想法是讓架構提供對外產生HTML的能力,或許有些使用者有這樣的需求。 另一方面,或許還有些使用者打算在Action的執行過程中,將原來較大的HTML頁面分塊輸出給用戶端。 BigPipe就使用了這種想法: 整個請求不用等到全部資料擷取成功後一次性輸出,而是將頁面按商務邏輯拆分,並在擷取到相應的資料後,立即向用戶端輸出部分區段。
其實HTML分塊輸出在ASP.NET中並不是什麼新的技術,而是在ASP.NET一出現時就已經存在了, 那就是在輸出的過程中不斷調用Response.Flush();
由於MyMVC將產生HTML做為一種基礎功能,因此在MyMVC中,只要您調用Response.Flush();便可以方便地實現分塊輸出。 不過,為了讓調用更簡單,我提供了二個輔助方法來簡化這個過程。
在PageExecutor類型中的ResponseWrite方法:
在UcExecutor類型中的ResponseWrite方法:
注意:由於這二個方法在內部使用了HttpContext.Current,因此如果在Action中調用它們,會造成Action不能支援單元測試。
回到頂部關於單元測試的支援
提到MVC思想,我想就不得不談單元測試了。
因為MVC的主要思想還是想把這三個字目對應的事物分開,以方便開發與測試。 這裡面,我認為尤其是View與Controller的分離最為重要,因為有UI的地方比較難測試, 反過來,如果沒有UI的東西就比較容易測試了。
不過,在ASP.NET中,影響單元測試的不僅僅只是UI元素,還有HttpContxt, HttpRequest, HttpResponse這之類的核心對象。 比如:即使我們將Controller放在類庫項目中實現,但在Action中還在訪問QueryString,Form,甚至發起重新導向的請求,你說這樣的代碼如何測試。
我認為判斷一個方法是否可支援單元測試有一個簡單的辦法:寫個控制台的程式去調用它,看它能否正常運行。
通常,使用者的輸入資料主要有三個來源:QueryString, Form, Cookie。而且前二者居多,Cookie則多用於儲存使用者喜好設定。 因此,在MyMVC中,可以讓Action不再去直接存取QueryString, Form,替代的方式是:將要讀取的名字做為C#方法的參數名明確指出。 這樣,Actioin中的代碼就遠離了QueryString, Form。至於Cookie的訪問,MyMVC則提供一個輔助類來支援訪問:
重新導向也是常見的需求。MyMVC則是通過提供RedirectResult來支援的:
說明:Ouput方法由架構調用,不影響Action的單元測試。
範例程式碼:
在ASP.NET項目開發過程中,還有一類需求較為常見,那就是:訪問一些當前環境變數。
MyMVC則是通過以下二個類型來處理的。
注意HttpContextHelper這個類,我將平時訪問的一些與請求或者與ASP.NET運行環境相關的屬性全部封裝在這裡了。 如果不夠,還可以繼續添加。有了這些代碼,我就可以簡單在控制台程式中調用它們(您也可以移到單元測試項目中):
用過ASP.NET MVC的人可能會問我:
為什麼不使用System.Web.Abstractions定義的那些類型,那樣不是更容易支援單元測試嗎?
是啊,我也知道那種做法的好處。但那樣做的工作量也會更大。
根據目前的MyMVC設計方式,如果要引入HttpContextBase, HttpRequestBase, HttpResponseBase這類對象, 就需要設計Controller基類,並且建立Controller的過程也會比目前複雜, 或者要把ASP.NET MVC那套建立Controller的過程搬過來,否則仍然會不完整。 然而,我還是打算自己再設計另一種簡單的方法 儘可能 地去解決這個問題。 上面的方法就是我的設計,雖然不夠完整,卻是簡單有效,而且測試代碼也會簡單很多。 另一方面,我不提供Controller基類,但可以設計諸如HttpContextHelper.Current這樣的訪問方式, 最終的結果仍然是可以支援單元測試的,況且HttpContextHelper.Current這種用法也不會讓人難以適應。 不提供還有另外的好處:允許設計自己的基類。
還有一點要補充的是:MyMVC架構內部也沒有使用System.Web.Abstractions,是的,我知道。
這也只能說:架構的代碼不能進行單元測試而已。 但不影響使用者的Action代碼的單元測試。
再說架構中的代碼有些也很難做單元測試,畢竟太依賴於ASP.NET,而且我沒那麼多的空閑時間以及驅動力。
MyMVC還有一個沒有支援的是檔案的上傳與下載。
這裡我來說說對於這塊功能訪如何去實現:
1. 可以直接存取HttpContext.Current ,並忽略這些代碼的單元測試能力。
2. 自行實現我前面沒有實現的HttpContextHelper.Current 。
是的,我的確沒有完成這個功能,而把它留給了使用者,抱歉。
回到頂部關於架構代碼與範例程式碼
在本文的未尾,我提供了MyMVC架構的代碼,以及全部範例程式碼。
以前我也提供過我的老版本架構的示範樣本, 我認為我已經考慮地相當周到了:
1. 沒有IIS,沒有VS,一樣可以運行我的DEMO,因為我把FishAspnetLoader放進去了,調用的BAT檔案也準備好了。
2. SQL SERVER如果支援【使用者執行個體】模式,部署會容易。
3. 在資料方面,我不但提供了mdf檔案,還提供了sql指令碼。
4. 還準備了一些說明檔案。
然而,事實卻沒有我想像那麼好,還是有很多人給我發郵件,問我樣本為什麼不能運行。
不能啟動並執行環境也是讓我完全沒有想到的:
1. 有人把它部署到了IIS6,副檔名的映射遇到問題。
2. 有人把它部署到了IIS7,可我沒有提供對IIS7的配置!
3. 有人沒有安裝SQL SERVER。這個只能是沒有辦法了!
4. 有人不能完成樣本程式所需的SQL SERVER配置。
5. 有人用VS2010開啟項目並升級了.net版本,遇到一些說不清楚的問題。
吸取前面的教訓後,這次我的樣本採用XML檔案做為資料來源,而且增加了IIS7的配置。
不過,有一點我不能替您設定的是XML檔案的寫入許可權。
如果資料不能儲存,請檢查目錄的寫入許可權,此時程式沒有任何提示。
再補充二點:
1. 如果您使用VS2010開啟樣本項目,請不要選擇升級.net版本,不要盲目點擊確定。
2. 如果在IIS中部署樣本網站遇到問題,那麼建議使用VS運行樣本網站。
如果您還有配置ASP.NET應用程式的問題,那麼請關注我的後續部落格。
下篇部落格我打算談一下在部署ASP.NET網站時,IIS6/7 以及SQL SERVER中必須知道的一些設定。
轉自:http://www.cnblogs.com/fish-li/archive/2012/02/21/2361982.html
寫自己的ASP.NET MVC架構(下)