使用自定義位置時如何在 asp.net core mvc 中指定視圖位置?
假設我有一個控制器,它使用基於屬性的路由來處理請求的 /admin/product 的 url,如下所示:
[Route("admin/[controller]")] public class ProductController: Controller { // GET: /admin/product [Route("")] public IActionResult Index() { return View(); } }現在假設我想將我的視圖組織在一個文件夾結構中,該結構大致反映了它們相關的 url 路徑。所以我希望這個控制器的視圖位於此處:
/Views/Admin/Product.cshtml更進一步,如果我有這樣的控制器:
[Route("admin/marketing/[controller]")] public class PromoCodeListController: Controller { // GET: /admin/marketing/promocodelist [Route("")] public IActionResult Index() { return View(); } }我希望框架在這裡自動查找它的視圖:
Views/Admin/Marketing/PromoCodeList.cshtml理想情況下,無論涉及多少 url 段(即嵌套的深度),用於通知視圖位置的框架的方法將以基於屬性的路由資訊的一般方式工作。
如何指示 Core MVC 框架(我目前正在使用 RC1)在這樣的位置查找控制器的視圖?
您可以通過實現視圖位置擴展器來擴展視圖引擎查找視圖的位置。下面是一些範常式式碼來展示該方法:
public class ViewLocationExpander: IViewLocationExpander { /// <summary> /// Used to specify the locations that the view engine should search to /// locate views. /// </summary> /// <param name="context"></param> /// <param name="viewLocations"></param> /// <returns></returns> public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { //{2} is area, {1} is controller,{0} is the action string[] locations = new string[] { "/Views/{2}/{1}/{0}.cshtml"}; return locations.Union(viewLocations); //Add mvc default locations after ours } public void PopulateValues(ViewLocationExpanderContext context) { context.Values["customviewlocation"] = nameof(ViewLocationExpander); } }然後在
ConfigureServices(IServiceCollection services)startup.cs 文件的方法中添加以下程式碼以將其註冊到 IoC 容器。之後立即執行此操作services.AddMvc();services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new ViewLocationExpander()); });現在,您可以將所需的任何自定義目錄結構添加到視圖引擎查找視圖和部分視圖的位置列表中。只需將其添加到
locationsstring[]. 此外,您可以將_ViewImports.cshtml文件放在同一目錄或任何父目錄中,它將被找到並與位於這個新目錄結構中的視圖合併。更新:
這種方法的一個好處是它提供了比後來在 ASP.NET Core 2 中引入的方法更大的靈活性(感謝@BrianMacKay 記錄了新方法)。例如,這種 ViewLocationExpander 方法不僅允許指定路徑層次結構來搜尋視圖和區域,還允許指定佈局和視圖組件。您還可以訪問完整
ActionContext的內容以確定合適的路線。這提供了很大的靈活性和功能。因此,例如,如果您想通過評估目前請求的路徑來確定適當的視圖位置,您可以通過context.ActionContext.HttpContext.Request.Path.
好消息…在 ASP.NET Core 2 及更高版本中,您不再需要自定義 ViewEngine 甚至 ExpandViewLocations。
使用 OdeToCode.AddFeatureFolders 包
這是最簡單的方法… K. Scott Allen 在 OdeToCode.AddFeatureFolders 為您提供了一個 nuget 包,該包很乾淨,包括對區域的可選支持。Github:https ://github.com/OdeToCode/AddFeatureFolders
安裝包,很簡單:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc() .AddFeatureFolders(); ... } ... }DIY
如果您需要對文件夾結構進行非常精細的控制,或者無論出於何種原因不允許/不想使用依賴項,請使用此選項。這也很容易,儘管可能比上面的 nuget 包更混亂:
public class Startup { public void ConfigureServices(IServiceCollection services) { ... services.Configure<RazorViewEngineOptions>(o => { // {2} is area, {1} is controller,{0} is the action o.ViewLocationFormats.Clear(); o.ViewLocationFormats.Add("/Controllers/{1}/Views/{0}" + RazorViewEngine.ViewExtension); o.ViewLocationFormats.Add("/Controllers/Shared/Views/{0}" + RazorViewEngine.ViewExtension); // Untested. You could remove this if you don't care about areas. o.AreaViewLocationFormats.Clear(); o.AreaViewLocationFormats.Add("/Areas/{2}/Controllers/{1}/Views/{0}" + RazorViewEngine.ViewExtension); o.AreaViewLocationFormats.Add("/Areas/{2}/Controllers/Shared/Views/{0}" + RazorViewEngine.ViewExtension); o.AreaViewLocationFormats.Add("/Areas/Shared/Views/{0}" + RazorViewEngine.ViewExtension); }); ... } ... }就是這樣!不需要特殊課程。
與 Resharper/Rider 打交道
額外提示:如果您使用的是 ReSharper,您可能會注意到在某些地方 ReSharper 無法找到您的視圖並給您帶來煩人的警告。要解決這個問題,請拉入 Resharper.Annotations 包並在您的 startup.cs(或其他任何地方)中為每個視圖位置添加以下屬性之一:
[assembly: AspMvcViewLocationFormat("/Controllers/{1}/Views/{0}.cshtml")] [assembly: AspMvcViewLocationFormat("/Controllers/Shared/Views/{0}.cshtml")] [assembly: AspMvcViewLocationFormat("/Areas/{2}/Controllers/{1}/Views/{0}.cshtml")] [assembly: AspMvcViewLocationFormat("/Controllers/Shared/Views/{0}.cshtml")]希望這能讓一些人免於我剛剛經歷過的幾個小時的挫敗感。:)