ASP.NET 核心 MVC 视图组件搜索路径

ASP.NET Core MVC View Component search path

在此处的文档中: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.2

The runtime searches for the view in the following paths:

/Views/{Controller Name}/Components/{View Component Name}/{View Name}
/Views/Shared/Components/{View Component Name}/{View Name}
/Pages/Shared/Components/{View Component Name}/{View Name}

如何在此处添加其他路径?

我希望将我的视图组件及其各自的控制器放在一个名为这样的组件的项目文件夹中。

/Components/{View Component Name}/{View Name}

我的动机:

我发现我的视图组件有自己的 JS 和 CSS 文件。我将所有 JS 打包并最小化到一个 site.min.js 中,并将所有 CSS 打包并最小化到它们的 site.min.css 中。 JS 总是类似于 $(function() { ... }) 而 CSS 总是以顺序无关紧要的方式编写,因此在不知道顺序的情况下捆绑所有内容不是问题。

其中一些视图组件有 javascripts,可以改变它们在服务器上的状态,例如AJAX 调用控制器的操作,其中 returns 一些 JSON 或整个视图组件的 HTML.

因为控制器只是一个 C# 类 它们可以在任何文件夹中,但是将具有相关 AJAX 操作的控制器移动到 "Views" 文件夹感觉很愚蠢。

最后我想要一个像这样的 "component"(不是真正的 "view component"):

/Components/SomeViewComponent/Default.cshtml
/Components/SomeViewComponent/SomeViewComponentController.cs
/Components/SomeViewComponent/SomeViewComponent.cs
/Components/SomeViewComponent/SomeViewComponent.css
/Components/SomeViewComponent/SomeViewComponent.js
/Components/SomeViewComponent/SomeViewComponent.en.resx
/Components/SomeViewComponent/SomeViewComponent.cs-CZ.resx

所以在深入aspnetcore仓库一个小时后,我发现组件的搜索路径是hardcoded然后结合普通视图搜索路径

// {0} is the component name, {1} is the view name.
private const string ViewPathFormat = "Components/{0}/{1}";

然后将此路径发送到视图引擎

result = viewEngine.FindView(viewContext, qualifiedViewName, isMainPage: false);

然后视图引擎使用可配置的视图路径生成完整路径。

  • Views/Shared/<strong>Components/Cart/Default</strong>.cshtml
  • Views/Home/<strong>Components/Cart/Default</strong>.cshtml
  • 地区/博客/Views/Shared/<strong>Components/Cart/Default</strong>.cshtml

如果你想将你的视图组件放入我想要的名为 "Components" 的根文件夹中,你可以这样做。

services.Configure<RazorViewEngineOptions>(o =>
{
    // {2} is area, {1} is controller,{0} is the action
    // the component's path "Components/{ViewComponentName}/{ViewComponentViewName}" is in the action {0}
    o.ViewLocationFormats.Add("/{0}" + RazorViewEngine.ViewExtension);        
});

在我看来,这有点丑陋。但它有效。

你也可以这样写你自己的扩展器。

namespace TestMvc
{
    using Microsoft.AspNetCore.Mvc.Razor;
    using System.Collections.Generic;

    public class ComponentViewLocationExpander : IViewLocationExpander
    {
        public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
        {
            // this also feels ugly
            // I could not find another way to detect
            // whether the view name is related to a component
            // but it's somewhat better than adding the path globally
            if (context.ViewName.StartsWith("Components"))
                return new string[] { "/{0}" + RazorViewEngine.ViewExtension };

            return viewLocations;
        }

        public void PopulateValues(ViewLocationExpanderContext context) {}
    }
}

并且在Startup.cs

services.Configure<RazorViewEngineOptions>(o =>
{
    o.ViewLocationExpanders.Add(new ComponentViewLocationExpander());   
});

您可以向 RazorViewEngineOptions 添加其他 查看位置格式。例如,要添加满足您要求的路径,您可以使用如下内容:

services
    .AddMvc()
    .AddRazorOptions(o =>
    {
        // /Components/{View Component Name}/{View Name}.cshtml
        o.ViewLocationFormats.Add("/{0}.cshtml");
        o.PageViewLocationFormats.Add("/{0}.cshtml");
    });

如上所示,views(使用控制器和操作时)和 page views(使用控制器和操作时)有不同的属性剃刀页面)。还有一个 属性 表示区域,但我在这个示例中将其省略以使其略短。

此方法的缺点是视图位置格式 不适用于视图组件。例如,当在 Home 中查找 Index 视图时,Razor 现在还会查找位于项目根目录的 Index.cshtml。这可能没问题,因为它是最后搜索的位置,我希望您不会在项目的根目录中看到任何视图,但它确实值得一提。