MathuSum Mut
MathuSum Mut

Reputation: 2825

NotSupportedException in Asp.NET Core MVC Razor Pages Rendering

I recently created a small ASP.Net Core MVC project, and placed all my .cshtml files in the Views directory. Then I using the following code in Startup.cs to change where Razor searches for the view files:

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

The .cshtml files map to a corresponding controller name, where all controllers contain a default "Index" action that simply returns the associated View().

This is the current routing:

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

In my eyes this configuration should work, however a "NotSupportedException" is being thrown by Razor whenever I try to invoke any page, and I am unsure how to debug this:

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)

I looked at the source code of EnsureRenderedBodyOrSections(), which is simply the following:

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 is apparently calling the function when it's not supposed to. What exactly is the issue that's at play here?

PS: This is the project on GitHub: https://github.com/mathusummut/nemesys-stackoverflow

Upvotes: 1

Views: 2677

Answers (1)

Arcanox
Arcanox

Reputation: 1547

The reason the EnsureRenderedBodyOrSections method is being called is because you're returning a View() result from a Controller, and this method is part of the rendering lifecycle for Views. Your Razor files, however, have @page directives at the top, which makes them into Page files for Razor Pages.

While you can use MVC Views and Razor Pages in the same application, they cannot be mixed for one request path. If you remove the @page directive from your View files, they will be compiled as Razor Views and not Razor Pages, so you will not get a NotSupportedException.

As a side note, I would recommend removing the line options.ViewLocationFormats.Clear(); and keep the default view location, so that you can use the Views/{Controller}/{Action} view location in the future while still being able to have a view file named Views/{Controller}. That way as you add more functionality to the now-MVC application, you can do so using traditional MVC patterns.

Upvotes: 2

Related Questions