寫自己的ASP.NET MVC框架(下)


上篇博客【寫自己的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個[PageUrl],這意味着其實我可以使用4種不同的URL都能訪問到這三個頁面, 而且每一個URL都會根據當前用戶所選擇的風格,呈現對應的頁面。

事實上,我還可以為這個Action指定更多的[PageUrl],讓它可以處理更多的URL。關於[PageUrl]的使用與設計目的,請繼續往下閱讀。

關於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中的一個調用引發的:


// 嘗試根據請求路徑獲取Action
InvokeInfo vkInfo = ReflectionHelper.GetPageActionInvokeInfo(virtualPath);

ReflectionHelper有個靜態構造函數,雖然上次我已貼出它的代碼,但那只是部分代碼,以下才是完整的初始化代碼:

從以上代碼可以看出,在初始化時,MyMVC加載了全部的PageAction ,而AjaxAction卻沒有采用這種方式來實現,為什么呢? 請繼續閱讀。

從URL到Action的映射過程

前面我們看到了MyMVC的初始化過程,其實是在ReflectionHelper的構造函數中完成的。 在這個初始化之后,MvcPageHandlerFactory調用ReflectionHelper.GetPageActionInvokeInfo(virtualPath)便可以得到要調用的Action的具體描述。 我稱這個過程為:從URL到Action的映射

GetPageActionInvokeInfo方法的實現代碼如下:

在介紹這個映射過程之前,讓我們再來回顧一下Action的聲明代碼:

通過ReflectionHelper構造函數中所完成的初始化過程,每個Action的描述會根據[PageUrl]的數量而生成多個字典條目, 因此,在GetPageActionInvokeInfo的實現過程中,也只是簡單的查找了這個字典而已,就可以得到所需要的調用信息,從面完成映射的過程。 整個過程可以用以下圖形來表示:

在上面的示例中,我使用了"/mvc/Customers"這種URL,顯然它並不符合我在web.config中為MvcPageHandlerFactory注冊時所指定的URL模式要求。 那么,又該如何處理呢?

雖然這種URL雖然沒有擴展名,但我仍然可以通過配置httpHandler的方式來解決,下面的配置就是我們需要的:

在介紹MvcPageHandlerFactory時,MyMVC提供了另一個方法TryGetHandler供外部使用。 因此,在示例網站中,我還可以在Global.asax中調用這個方法來解決前面的那個問題:

對於切換HttpHandler的操作,我有以下建議:
1. 盡量放在HttpModule中去實現。因為可以通過修改配置來切換規則(啟用或者禁止),所以會比較靈活。
2. 如果可以通過HttpHandler映射能實現的,盡量首選HttpHandler映射方式。原因:更快,更標准。

PageUrl的設計思想

在前面的示例代碼中,我為一個Action添加多個[PageUrl],來標記這個Action可以處理多個URL, 因此,一個Action能處理哪些URL是通過指定[PageUrl]來實現的。

為什么要叫【PageUrl】?
我想或許有些人會有這個疑問。
下面我就來回答這個問題,也可以讓大家了解我設計PageUrl的原因:
1. 我們請求一個URL通常是為了得到一個頁面顯示,因此可以認為一個URL最終可以表示成一個頁面。
2. 我也想過使用[Url]這種名稱,但感覺太短了,而且Ajax請求也有URL,那么必須顯式地加以區分。
所以,我最終決定使用[PageUrl]這個名字。

在Ajax部分,我認為通常只需要完成獲取數據以及處理提交數據的功能就可以了。 因此,絕大多數情況下是不要需View的,而且,一個功能與一個URL對應,這樣還可以簡化問題。 所以,在Ajax部分,我提倡在URL中直接指出要調用哪個Controller中的哪個Action。

在Page部分,事實上也需要一個Action,本來也是可以繼續使用這種做法的, 不過,我並沒有這種做,理由如下:
1. 我們創建View其實也是創建Page,使用Page的路徑不是更好嗎?而且WebForm的粉絲或許會更喜歡。
2. 多URL的匹配功能。后面會有詳細說明。

由於以上種種原因,我將[PageUrl]設計成與[Action]是獨立關系,並且[PageUrl]可以多次指定的。

注意:
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中,可以簡單地處理這個問題,就像下面的這個示例一樣:

這個“客戶管理”頁面可能經過了多次重構,沒關系,只要把各個版本的地址用[PageUrl]標識出來就可以了,完全不用前面所說的兼容方案, 因此,在URL的兼容處理上沒有任何負擔,也不會影響性能。

說明:[PageUrl]的順序並不重要,可以隨意調整。

對身份認證的支持

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. 必須有一個有效的[PageUrl]的修飾屬性指出可以處理的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又有[PageUrl]來指示可以處理哪些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中必須知道的一些設置。

點擊此處下載示例代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM