Asp.NET 核心 MVC Razor 页面呈现中的 NotSupportedException

NotSupportedException in Asp.NET Core MVC Razor Pages Rendering

我最近创建了一个小型 ASP.Net 核心 MVC 项目,并将我所有的 .cshtml 文件放在 Views 目录中。然后我在 Startup.cs 中使用以下代码更改 Razor 搜索视图文件的位置:

services.Configure<RazorViewEngineOptions>(options => {
    //{2} is area, {1} is controller, {0} is the action    
    options.ViewLocationFormats.Clear();
    options.ViewLocationFormats.Add("/Views/{1}" + RazorViewEngine.ViewExtension);
});

.cshtml 文件映射到相应的控制器名称,其中所有控制器都包含一个默认的 "Index" 操作,该操作只是 returns 关联的 View()。

这是当前路由:

app.UseMvc(routes => {
    routes.MapRoute(
        name: "default",
        template: "{controller=Index}/{action=Index}/{id?}");
});

在我看来,此配置应该有效,但是每当我尝试调用任何页面时,Razor 都会抛出 "NotSupportedException",我不确定如何调试它:

NotSupportedException: Specified method is not supported.
Microsoft.AspNetCore.Mvc.RazorPages.PageBase.EnsureRenderedBodyOrSections()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync<TFilter, TFilterAsync>()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

我看了一下EnsureRenderedBodyOrSections()的源码,简单来说就是下面这样:

public override void EnsureRenderedBodyOrSections() {
    //This will never be called by MVC. MVC only calls this method on layout pages, and a Page can never be a layout page.
    throw new NotSupportedException();
}

MVC 显然在不该调用的时候调用了该函数。这里的问题到底是什么?

PS:这是 GitHub 上的项目:https://github.com/mathusummut/nemesys-Whosebug

调用 EnsureRenderedBodyOrSections 方法的原因是因为您要从控制器返回 View() 结果,并且此方法是 Views 渲染生命周期的一部分。但是,您的 Razor 文件在顶部有 @page 指令,这使它们成为 Razor Pages.

的页面文件

虽然您可以在同一个应用程序中使用 MVC 视图和 Razor 页面,但不能将它们混合用于一个 请求路径。如果您从视图文件中删除 @page 指令,它们将被编译为 Razor 视图而不是 Razor 页面,因此您将不会得到 NotSupportedException.

作为旁注,我建议删除行 options.ViewLocationFormats.Clear(); 并保留默认视图位置,以便您将来可以使用 Views/{Controller}/{Action} 查看位置,同时仍然能够拥有名为 Views/{Controller} 的视图文件。这样,当您向现在的 MVC 应用程序添加更多功能时,您可以使用传统的 MVC 模式来实现。