Tech Tip: Returning an MVC View from .Net Core Middleware

Updated on: 03 Jul 2020

A recent project required me to return an MVC view from the middleware.

The first thing we need to is create a middleware class. A bare middleware class will look something like below:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace MinatCoding
{
    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;

        public MyCustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // Call the next delegate/middleware in the pipeline
            await _next(context);
        }
    }
}
Middleware.cs

To learn more about .Net core middleware and its different parts, you may refer to https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1

The next step is to create a ViewResult object for the middleware to return which looks something like:

var viewResult = new ViewResult()
{
    ViewName = //path to view e.g. ~/Views/Tests/Index.cshtml,
};
ViewResult.cs

View models are stored in the Model property of a ViewDictionary object which is stored in the ViewResult's ViewData property.

There are two constructors to create a ViewDictionary object:

  1. The first constructor requires another ViewDictionary as a source.
  2. The second constructor requires a MetaDataProvider and a ModelStateDictionary.

For our case, we are goign to use the second constructor. For ModelStateDictionary, we can simply instantiate an empty ModelStateDictionary. Luckily for us, a basic MetaDataProvider, the EmptyModelMetadataProvider, is provided as part of the AspNet.Core.Mvc library.

Your ViewDataDictionary instantiation class should look like below after we put everything together.

var ViewDataDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(),
                                new ModelStateDictionary())

The ViewDataDictionary should then be set as the ViewResult's ViewData property value.

The next thing we need to is set up the executor service. The executor service will take the ViewResult and execute it to generate a response for the current Http Context.

The code for setting up the executor service:

var executor = context.RequestServices
    .GetRequiredService<IActionResultExecutor<ViewResult>>();
var routeData = context.GetRouteData() ?? new RouteData();
var actionContext = new ActionContext(context, routeData,
    new Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor());
Executor.cs

After setting up the executor, we then run the following method of the executor service to execute the view result and send it as a response of the current the HttpContext:

await executor.ExecuteAsync(actionContext, viewResult);
Execute.cs

Your entire middleware class should look something like below:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace MinatCoding
{
    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;

        public MyCustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var viewResult = new ViewResult()
            {
                ViewName = //path to view e.g. ~/Views/Tests/Index.cshtml,
            };
            var viewDataDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(),
                                new ModelStateDictionary());
            viewDataDictionary.Model = //your model
            viewResult.ViewData = viewDataDictionary;

            
            var executor = context.RequestServices
            .GetRequiredService<IActionResultExecutor<ViewResult>>();
            var routeData = context.GetRouteData() ?? new RouteData();
            var actionContext = new ActionContext(context, routeData,
            new Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor());

            await executor.ExecuteAsync(actionContext, viewResult);           
        }
    }
}
MyViewResultMiddleware.cs

Here are some more posts