天天看點

(五) ViewEngine 深入解析與應用執行個體(mvc)

本文講解ViewEngine的作用, 并且深入解析了實作ViewEngine相關的所有接口和類, 最後示範了如何開發一個自定義的ViewEngine. 本系列文章已經全部更新為ASP.NET MVC 1.0版本.希望大家多多支援!

首先注意: 我會将大家在MVC之前一直使用的ASP.NET頁面程式設計模型稱作ASP.NET WebForm程式設計模型.

上一講中我們已經學習了如何向View傳遞Model, 以及如何在View中使用Model對象. 目前為止我們使用的都還是ASP.NET WebForm的頁面模型,比如aspx頁面,使用者控件,母版頁等. 最後這些頁面中都要轉換為HTML代碼. 比如頁面中的内嵌代碼:

你是否思考過, 為何頁面會支援<% %>這種文法? 為何最後一個aspx頁面會在浏覽器中以HTML代碼的形式展現?

有人會回答這是ASP.NET自帶的文法和功能. 沒有錯, ASP.NET幫我們做了編譯頁面, 輸出HTML, 傳回HTML給用戶端浏覽器等一系列工作.但是這些工作在MVC架構中有很多是屬于View角色的職責. 為了繼續使用原有的ASP.NET WebForm頁面引擎, ASP.NET MVC抽象出來了ViewEngine這個角色. 顧名思義ViewEngine即視圖引擎, 其主要作用就是找到View對象,  編譯View對象中的語言代碼(執行語言邏輯), 并且輸出HTML. 下面講解的WebFormViewEngine就是使用ASP.NET WebForm的頁面編譯/呈現功能實作的.

下面将講解和ViewEngine有關的各個接口和類.

IView接口是對MVC結構中View對象的抽象, 此接口隻有一個方法:

Render方法的作用就是展示View對象, 通常是将頁面HTML寫入到Writer中供浏覽器展示.

在本系列第三篇文章中我曾經分析過, 雖然IView對象是MVC中View角色的抽象, 并且提供了Render方法, 但是實際上真正的View角色的顯示邏輯在ViewPage/ViewUserControl類中. 這是由于ASP.NET MVC提供的WebFormViewEngine視圖引擎是使用原有的ASP.NET Web From的頁面顯示機制, 我們無法直接将WebForm模型中的頁面轉化為IView對象.

于是最後使用了一個折中的辦法:

在IView對象的Render方法中調用WebForm頁面的Render方法. WebFormView是目前ASP.NET MVC中唯一實作了IView接口的類

是以如果我們使用自定義的ViewEngine引擎, 就可以直接建立一個實作了IView接口的類實作Render方法.

ViewEngine即視圖引擎, 在ASP.NET MVC中将ViewEngine的作用抽象成了 IViewEngine 接口.

雖然IViewEngine的職責是尋找View對象, 但是其定義的兩個方法:

FindPartialView

FindView

傳回的結果是ViewEngineResult對象, 并不是View對象. 我們可以将ViewEngineResult了解為一次查詢的結果, 在ViewEngineResult對象中包含有本次找到的IView對象.

ASP.NET MVC 提供了下面兩個實作了IViewEngine接口的類:

VirtualPathProviderViewEngine

WebFormViewEngine

WebFormViewEngine是VirtualPathProviderViewEngine的派生類.

VirtualPathProviderViewEngine類實作了FindPartialView/FindView方法, 更夠根據指定的路徑格式搜尋頁面檔案, 并且使用了提供了Cache機制緩存資料. 注意因為使用的是ASP.NET Cache,依賴HttpContext對象, 這就導緻Cache無法在WebService或者WCf等項目中使用. VirtualPathProviderViewEngine尋找頁面的方法依賴下面三個屬性:

MasterLocationFormats

ViewLocationFormats

PartialViewLocationFormats

在VirtualPathProviderViewEngine中隻定義了這三個屬性, 具體的值在派生類WebFormViewEngine中指定:

上面的代碼中我們可以一步了然ViewEngine都搜尋哪些路徑.甚至還可以添加我們自己的路徑和檔案類型.

因為有了VirtualPathProviderViewEngine類, 在開發自定義的ViewEngine時不需要再編寫搜尋View檔案的邏輯了.隻需要定義搜尋路徑即可. 如果不使用ASP.NET WebForm的頁面顯示方式, 就需要自己定義的View對象如何顯最後轉化為HTML代碼.

在後面的執行個體中會示範建立一個我們自定義的ViewEngine.

ViewEngineResult是ViewEngine尋找View的查詢結果.ViewEngineResult類沒有派生類,  也就是說不同的ViewEngine傳回的結果都是ViewEngineResult對象.

ViewEngineResult類有一個很重要的構造函數:

以WebFormViewEngine為例, 在WebFormViewEngine類中定義了 MasterLocationFormats/ViewLocationFormats /PartialViewLocationFormats , 在調用FindPartialView/FindView方法時, 首先找到View對象的磁盤路徑, 然後使用CreatePartialView/CreateView方法将磁盤路徑轉化實作了IView接口的WebFormView對象.

WebFormView中依然儲存這頁面對象的磁盤路徑, 在調用Render時會根據磁盤路徑建立ViewPage對象, 調用頁面的Render方法.ASP.NET MVC編譯頁面時, 使用了.NET Framework 2.0以上的版本中提供的根據虛拟路徑編譯頁面的函數:

命名空間為System.Web.Compilation.

ViewEngineCollection是IViewEngine對象的集合類. 在我們的系統中可以使用多個ViewEngine,  在尋找時會傳回第一個比對的ViewEngineResult, 下面是ViewEngineCollection類的Find方法代碼:

通過上面的代碼我們了解到, ViewEngineCollection會首先從Cache中搜尋, 如果沒有搜尋到結果,則根據路徑格式搜尋. 如果最後還是沒有搜尋到View對象則抛出找不到View的異常.是以雖然我們可以添加多個ViewEngine, 但是永遠不要為兩個ViewEngine指定同樣的搜尋格式(路徑+檔案類型), 因為如果出現一個頁面對象符合兩個ViewEngine的搜尋格式的情況, 将無法控制使用哪一個ViewEngine輸出頁面.

在ViewBaseResult.ExecuteResult() 方法中, 調用了ViewEngineCollection.Find方法擷取ViewEngineResult對象,并調用其中的IView.Render()方法完成View對象的顯示.

下面通過示例示範如何開發自己的ViewEngine.其中要用到StringTemplate這個模闆引擎, 在老趙的的MVC視訊教程中也使用的此引擎示範ViewEngine. StringTemplate負責翻譯一個模闆頁上面的占位符(aspx頁面中的内嵌代碼), 輸出HTML.目前在StringTemplate的官方網站上已經提供了針對Asp.Net Mvc的ViewEngine.但是官方的ViewEngine模闆沒有使用VirtualPathProviderViewEngine基類.下面我将提供一種不能說更好但至少是另一種實作的StringTemplateViewEngine.其中需要使用StringTemplate的模版功能.

要開發一個自己的ViewEngine, 首先要建立實作了IView接口的類, 在此我們建立了名為StringTemplateView的類

StringTemplateView是在StringTemplate視圖引擎中View角色的抽象, 是以功能是實作呈現頁面的Render方法. StringTemplate的核心功能就是一套自己定義的模闆輸出引擎, 是以在構造StringTemplateView對象是必須傳入一個StringTemplate執行個體,在Render時隻是調用StringTemplate對象的模闆輸出方法.

有了IView對象. 接下來就要實作最核心的IViewEngine接口. 在具體的StringTemplateViewEngine類中, 要傳回一個帶有StringTemplateView對象的ViewEngineResult.

在我的實作方法中,使用了ASP.NET MVC已經提供的VirtualPathProviderViewEngine類作為我們的基類. VirtualPathProviderViewEngine類實作了IViewEngine接口的方法, 提供了在程式中尋找View實體檔案路徑的機制, 搜尋時要使用在派生類中指派的搜尋路徑.

下面是我們的StringTemplateViewEngine類實作:

注意首先在我們的StringTemplateViewEngine中,提供了搜尋模闆檔案的路徑,即先從View/{controller}中搜尋,再從View/Share中搜尋. 這樣VirtualPathProviderViewEngine基類的方法就可以找到我們.st模闆檔案的具體路徑, 然後使用StringTemplateViewEngine中提供的建立StringTemplateView的方法, 根據具體路徑建立StringTemplateView對象.

在一些開源的ViewEngine中,尤其是MvcContrib項目中的ViewEngine都将建立View對象的功能放在一個ViewFactory類中, 個人認為這個更好的設計, 但是由于我們的StringTemplateViewEngine要繼承VirtualPathProviderViewEngine, 是以沒辦法拆分建立View的方法.

至此我們已經完成了StringTemplateViewEngine的全部工作.

首先做一些準備工作. 因為我們的StringTemplate模闆檔案字尾是".st", 裡面寫的大部分都是HTML代碼. 預設情況下Visual Studio是不會在編輯.st功能的時候提供智能感覺支援的. 但是可以通過如下設定實作:

單擊菜單中的"工具"->"選項":

(五) ViewEngine 深入解析與應用執行個體(mvc)

在"文本編輯器"的檔案擴充名中, 如圖所示的為.st擴充名增加"HTML編輯器".

接下來在.st檔案中就可以識别HTML代碼了:

(五) ViewEngine 深入解析與應用執行個體(mvc)

StringTemplate引擎支援模闆的嵌套, 是以可以講兩個頁面公用的菜單欄放在menu.st檔案中. 而且我們将此檔案放在share檔案夾中以便供所有模闆頁調用. menu.st檔案代碼如下:

在Controller檔案夾中, 建立StringTemplateController用于跳轉到我們的模闆頁:

在View檔案夾中建立StringTemplate檔案夾, 添加一個HelloST.st檔案:

示例中的代碼十分簡單, "$msg$"是StringTemplate的模闆語言, 可以識别名稱為"msg"的變量. "$Views/Shared/menu()$"也是StringTemplate中的文法, 作用是加載名為menu的模闆.

雖然Controller和View檔案都建立好了, 但是因為ASP.NET MVC預設的視圖引擎是WebFormViewEngine, 但是可以同時使用多個視圖引擎, 比如可以為所有".st"字尾名的檔案使用StringTemplateViewEngine視圖引擎.在Global.asax檔案中, 在程式啟動時注冊我們的ViewEngine:

現在, 通路"localhost/StringTemplate/HelloST"

,就可以看到我們的自定義的模闆引擎的輸出結果了:

(五) ViewEngine 深入解析與應用執行個體(mvc)

在文章最後會提供本執行個體附帶StringTemplateViewEngine的完整源代碼.

除了自己開發, 目前已經有了很多為ASP.NET MVC提供的ViewEngine:

MVCContrib項目中的ViewEngine:

SparkViewEngine(不推薦)

BrailViewEngine

XsltViewEngine

StringTemplateViewEngine

這是StringTemplate項目為ASP.NET MVC開發的ViewEngine, 官方以及下載下傳網址是

<a href="http://www.stringtemplate.org/" target="_blank">http://www.stringtemplate.org/</a>

另外在著名的MonoRail項目中, 還有一些類似于StringTemplate的頁面顯示引擎, 雖然都沒有為ASP.NET MVC開發專門的ViewEngine, 但是還是很有參考價值的.我們可以用上面介紹的方法, 在頁面顯示引擎的基礎上自己開發ASP.NET MVC的ViewEngine:

MonoRail項目中的三個ViewEngine:

AspNetViewEngine:用傳統的.aspx檔案做模闆, 可以照常使用aspx文法和伺服器控件, 但是由于Webform的生命周期和MonoRail完全不同, 有時候會讓人覺得别扭, 有部分特性也受到了限制.

NVelocityViewEngine: 用NVelocity做模闆引擎, 需要學習VTL文法, 但是使用很簡單, 特别是很多java程式員已經熟悉velocity. 簡單的文法也強迫程式員把邏輯和界面很好的分離開來, 友善跟美工配合.

BrailViewEngine:基于Boo的模闆引擎, Boo是一種文法類似python的.NET語言, 據MonoRail的參考說, Brail引擎是功能最強, 性能最好的選擇, 但Boo是一種陌生的語言, 這成了Brail引擎應用的最大障礙.

本篇文章詳細介紹了ViewEngine相關類, 已經如何開發自己的ViewEngine. 花了2周時間創作完成, 讓大家久等了. 說道最近部落格園首頁的文章問題, 我覺得一篇文章除了要有知識點, 還有能夠很好的講解, 讓大家明白比讓自己明白更重要.我沒有為了速度草草發表文章,就是希望寫出來的東西能夠有資格發表到部落格園首頁.

部落格園大道至簡

<a href="http://www.cnblogs.com/jams742003/" target="_blank">http://www.cnblogs.com/jams742003/</a>

轉載請注明:部落格園