Could not find an IRouter associated with the ActionContext when rendering a view in the middleware

188 Views Asked by At

I'm going to render a razor page in the middleware of asp.net core

Although the function I wrote works in all controllers, the following error is displayed in the middleware

Could not find an IRouter associated with the ActionContext.

I also tried the IHttpContextAccessor injection but got no results!

Here are my codes:

Startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //...
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseHsts();

        app.UseResponseCompression();

        app.UseHttpsRedirection();

        app.UseStaticFiles();

        app.UseTest();

        app.UseResponseCaching();

        app.UseAuthentication();

        app.UseAuthorization();

        app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
    }
}

Test.cs:

public static class Test
{
    public static IApplicationBuilder UseTest(this IApplicationBuilder app)
    {
        return app.UseMiddleware<TestMiddleware>();
    }
}

TestMiddleware.cs:

public class TestMiddleware
{
    private readonly RequestDelegate _next;

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

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

        if (context.Response.StatusCode < 400)
        {
            return;
        }

        var body = await razorRenderer.ToStringAsync(context, "IntelligentView", new IntelligentViewModel());

        await context.Response.WriteAsync(body);
    }
}

RazorRenderer.cs:

public class RazorRenderer
{
    private readonly IRazorViewEngine _razorViewEngine;
    private readonly ITempDataProvider _tempDataProvider;

    public RazorRenderer(
        IRazorViewEngine razorViewEngine,
        ITempDataProvider tempDataProvider)
    {
        _razorViewEngine = razorViewEngine;
        _tempDataProvider = tempDataProvider;
    }

    public async Task<string> ToStringAsync<TModel>(HttpContext context, string viewName, TModel model)
    {
        var actionContext = new ActionContext(
            context,
            context.GetRouteData(),
            new ActionDescriptor());
        var view = FindView(actionContext, viewName);
        var viewData = new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary())
        {
            Model = model
        };
        var tempData = new TempDataDictionary(context, _tempDataProvider);

        await using var stringWriter = new StringWriter();

        var viewContext = new ViewContext(
            actionContext,
            view,
            viewData,
            tempData,
            stringWriter,
            new HtmlHelperOptions()
        );

        await view.RenderAsync(viewContext);

        return stringWriter.ToString();
    }

    private IView FindView(ActionContext actionContext, string viewName)
    {
        var getPartialResult = _razorViewEngine.GetView(null, viewName, false);

        if (getPartialResult.Success)
        {
            return getPartialResult.View;
        }

        var findPartialResult = _razorViewEngine.FindView(actionContext, viewName, false);

        if (findPartialResult.Success)
        {
            return findPartialResult.View;
        }

        var searchedLocations = getPartialResult.SearchedLocations.Concat(findPartialResult.SearchedLocations);
        var errorMessage = string.Join(
            Environment.NewLine,
            new[]
            {
                $"Unable to find partial '{viewName}'. The following locations were searched:"
            }.Concat(searchedLocations));

        throw new InvalidOperationException(errorMessage);
    }
}
0

There are 0 best solutions below