動態修改 View Engine 的搜尋順序

應該有寫 ASP.NET MVC 都看過這個錯誤畫面,其實這邊就是預設的搜尋順序啦,如果找不到的時候就會出現下面的畫面。這個是 Home/Index 執行時候的畫面,預設動作會先找尋 WebPage View 再找尋 Razor (MVC 3 Later) View ,優先順序是 Views/{Controller}/{Action} > Views/Shared/{Action} 。
image
今天剛好遇到一個多語系支援的需求,但是又不是每一個頁面都支援全部的語系,因為內容頁面有些是另外由人工翻譯完才加入,希望在還沒翻譯完之前先顯示原文,可以根據直接加入檔案新增語言,所以我的想法先找語系資料夾下面的 View ,如果沒有的話再用原本的搜尋方式尋找。
image
如果在不同語系有另外設定的 View 就針對語系先套用,沒有的話就是預設的輸出了,這樣感覺很方便,而且假如說原本頁面的英文版本還沒翻譯好,也可以等翻譯完再加入,還沒加入之前瀏覽也不會也掛掉的問題。但是要這樣做預設的功能沒有支援,所以必須修改 View Engine 尋找 View 的順序,讓他在找尋的時候先根據語系尋找。

建立自訂 View Engine 繼承 RazorViewEngine

public class MultiLanguegeRazorViewEngine : RazorViewEngine

這個地方因為我只有用到 Razor 的 View 所以就只需要一個自訂的 View Engine 繼承 RazorViewEngign,如果有用到 Aspx 或是 ascx 的 View 就是需要另外一個 View Engine 繼承 WebFormViewEngine。最後在依照想要哪個View Engine 先做找尋的動作加入 ViewEngines。

修改 View Engine Formats 屬性

  • AreaMasterLocationFormats (找尋 Area 裡面的 MasterPage)
  • AreaPartialViewLocationFormats (找尋 Area 裡面的 ParttialView)
  • AreaViewLocationFormats (找尋 Area 裡面的 View)
  • MasterLocationFormats (找尋 MasterPage )
  • PartialViewLocationFormats (找尋 ParticalView)
  • ViewLocationFormats (找尋 View)

View Engine 有這幾個 Format 屬性,在建構子的時候修改,在不同情況下會根據不同 Format 做找尋的動作。如果有需要修改的 Format 在額外去做設定,不然就會以繼承的為準。

public MultiLanguegeRazorViewEngine()
: base()
{
ViewLocationFormats = new[] {
"~/Views/{1}/%1/{0}.cshtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/%1/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
};

PartialViewLocationFormats = new[] {
"~/Views/{1}/%1/{0}.cshtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/%1/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
};
}

Override 原本的方法

在搜尋 View 的時候會去呼叫 ViewEngine 裡面的這幾種方法,在呼叫的時候把剛剛加入 Formats 裡面的 “%1” 替換成想要的位置,會在這邊作處理是因為 controller 處理完之後再呼叫 View 的時候會將 controllerContext 參數傳進來,這樣就可以從傳進來的 controllerContext 作為判斷依據來動態改變 View 的搜尋順序。

protected override 
IView CreatePartialView(ControllerContext controllerContext,
string partialPath)

{

string Lang = controllerContext.Controller.ViewBag.Lang;

return base.CreatePartialView(controllerContext, partialPath.Replace("%1", Lang));
}



protected override
IView CreateView(ControllerContext controllerContext,
string viewPath, string masterPath)

{

string Lang = controllerContext.Controller.ViewBag.Lang;

return base.CreateView(controllerContext, viewPath.Replace("%1", Lang), masterPath.Replace("%1", Lang));
}



protected override bool FileExists(
ControllerContext controllerContext, string virtualPath)

{

string Lang = controllerContext.Controller.ViewBag.Lang;

return base.FileExists(controllerContext, virtualPath.Replace("%1", Lang));
}

在 Global 修改 View Engine 成自訂的 View Engine

最後在Application_Start()的地方先清除預設的ViewEngines,在重新加入剛剛設定的MultiLanguegeRazorViewEngine。

protected void Application_Start()
{

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new
MultiLanguegeRazorViewEngine());
}

這樣就大功告成了,ViewEngine 搜尋 View 的邏輯就會根據自訂的路徑順序來做搜尋的動作。

P.S.

Formats 參數設定是

  • {0}: ActionName
  • {1}:ControllerName
  • {2}:AreaName

有用到 AreaName 的地方就只有 Area 相關的 Formats,不是每個 Formats 都需要。


http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx
Custom ViewEngine ASP.NET MVC 3 – Stack Overflow
Creating your own MVC View Engine For MVC Application

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *