ViewEngine 是 MVC 中「尋找、編譯、渲染視圖」的核心抽象。本文解析 IView、IViewEngine、VirtualPathProviderViewEngine、WebFormViewEngine、RazorViewEngine 關係,並完整示範如何開發**自訂 ViewEngine** (支援自訂副檔名、非標準視圖位置、編譯緩存),附原始碼級剖析。
IView
└─ Render(ViewContext, TextWriter) // 唯一職責:輸出 HTML
IViewEngine
├─ FindView(ControllerContext, viewName, masterName, useCache) → ViewEngineResult
└─ FindPartialView(ControllerContext, partialViewName, useCache) → ViewEngineResult
ViewEngineResult
├─ View (IView) // 找到的視圖
├─ ViewEngine (IViewEngine) // 來源引擎
└─ SearchedLocations (string[]) // 搜尋過的路徑 (供除錯)
VirtualPathProviderViewEngine (抽象基類)
├─ ViewLocationFormats / MasterLocationFormats / PartialViewLocationFormats (搜尋路徑模式)
├─ FileExtensions (副檔名)
└─ CreateView / CreatePartialView (工廠方法)
WebFormViewEngine : VirtualPathProviderViewEngine // .aspx/.ascx/.master
RazorViewEngine : VirtualPathProviderViewEngine // .cshtml/.vbhtml
FindView 依 ViewLocationFormats (預設 ~/Views/{1}/{0}.aspx, ~/Views/Shared/{0}.aspx...) 尋找檔案。CreateView → 建立 WebFormView (實作 IView)。WebFormView.Render 內部委託 BuildManager.CreateInstanceFromVirtualPath 編譯/載入頁面類別,呼叫其 Render。masterName)、部分視圖 (FindPartialView)。public class HtmlTemplateViewEngine : VirtualPathProviderViewEngine
{
public HtmlTemplateViewEngine()
{
// 定義搜尋路徑與副檔名
ViewLocationFormats = new[] { "~/Views/{1}/{0}.html", "~/Views/Shared/{0}.html" };
PartialViewLocationFormats = new[] { "~/Views/Shared/Partials/{0}.html" };
FileExtensions = new[] { "html" };
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new HtmlTemplateView(viewPath);
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new HtmlTemplateView(partialPath);
}
}
// 簡易 IView 實作:讀取 .html 檔案,做標記替換 (如 {{Model.Title}})
public class HtmlTemplateView : IView
{
private readonly string _viewPath;
public HtmlTemplateView(string viewPath) => _viewPath = viewPath;
public void Render(ViewContext viewContext, TextWriter writer)
{
string template = File.ReadAllText(viewContext.HttpContext.Server.MapPath(_viewPath));
// 簡易模型綁定:替換 {{PropertyName}}
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(viewContext.ViewData.Model))
{
object val = prop.GetValue(viewContext.ViewData.Model);
template = template.Replace($"{{{{{prop.Name}}}}}", val?.ToString() ?? "");
}
writer.Write(template);
}
}
protected void Application_Start()
{
ViewEngines.Engines.Clear(); // 可選:移除預設引擎
ViewEngines.Engines.Add(new HtmlTemplateViewEngine());
// 或保留預設,僅添加:ViewEngines.Engines.Insert(0, new HtmlTemplateViewEngine());
}
| 主題 | 關鍵點 |
|---|---|
| 編譯快取 | 繼承 BuildManagerCompiledView 或實作 IView 內部快取編譯結果 (如 Razor 預編譯)。 |
| 依賴注入整合 | ViewEngine 建構函式注入 IServiceProvider,視圖渲染時解析服務。 |
| 主題/裝置切換 | 覆寫 FindView,依 ControllerContext.HttpContext.Request (User-Agent, Cookie) 動態調整搜尋路徑。 |
| 除錯支援 | ViewEngineResult.SearchedLocations 回傳嘗試路徑,ViewNotFoundException 會顯示完整列表。 |
ViewEngine 將「視圖尋找與渲染」標準化、可替換。理解 IView/IViewEngine/VirtualPathProviderViewEngine 分層,即可開發支援**任意模板語法、儲存介質、主題切換**的自訂引擎。Razor 的成功證明:好的 ViewEngine = **語法簡潔 + 編譯快取 + 工具支援 (IntelliSense)**。自訂引擎除非有特殊需求 (舊系統遷移、非標準模板),否則優先使用內建 Razor。