'how to return view from custom middleware?

My goal is intercept 404 status code using middleware and return custom view (interrupt execution).

I tried some examples. And, only await context.Response.WriteAsync("test 404 response error"); works. But this is not I need.

How to achieve this?

My next example does not work (I mean I got blank page or default Chrome not found page):

public class CustomError404Middleware
{
    private readonly RequestDelegate _next;

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

    public async Task Invoke(HttpContext context)
    {
        await _next(context);

        if (context.Response.StatusCode == 404 && !context.Response.HasStarted)
        {
            //Re-execute the request so the user gets the error page
            var originalPath = context.Request.Path.Value;
            context.Items[nameof(Defaults.ORIGINAL_PATH)] = originalPath;
            context.Request.Path = "Error/404";
            // context.Request.Path = "/Error/404";

            await _next(context);
        }
    }
}

Configuration:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseMigrationsEndPoint();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            // app.UseStatusCodePages();

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseMiddleware<RequestLoggerMiddleware>();
            app.UseMiddleware<CustomError404Middleware>();

.............. skipped (Rollbar, CORS) ..............

            app.UseSession();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "dashboard",
                    pattern: "{controller=Home}/{action=Index}");
                endpoints.MapRazorPages();
                endpoints.MapMetrics();
            });

Controller and view:

    [AllowAnonymous]
    public class ErrorController : Controller
    {
        [ActionName("404")]
        public IActionResult PageNotFound()
        {
            ViewData[nameof(Defaults.ORIGINAL_PATH)] = "unknown";

            if (HttpContext.Items.ContainsKey(nameof(Defaults.ORIGINAL_PATH)))
            {
                ViewData[nameof(Defaults.ORIGINAL_PATH)] = HttpContext.Items[Defaults.ORIGINAL_PATH] as string;
            }

            return View();
        }
    }
@ViewData[nameof(Defaults.ORIGINAL_PATH)]

<div>
    <p>
        test
    </p>
</div>


Solution 1:[1]

To achieve a similar end, I created a helper method in my middleware class

private Task GetViewResultTask(HttpContext context, string viewName)
{
    var viewResult = new ViewResult()
    {
        ViewName = viewName
    };

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

and then from the middleware where I actually wanted to return the view (after some conditional logic maybe, or doing some other things), I would

await GetViewResultTask(context, "~/Views/Home/NotRegistered.cshtml");
return;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 TZHX