c# - 在 core 3.0 中将 View 渲染为字符串 : Could not find an IRouter associated with the ActionContext

标签 c# asp.net-core asp.net-core-3.0

我想要什么:

在我的应用程序中,我想为我的电子邮件使用模板。不幸的是,我之前在另一个项目中使用的代码不再工作。

抛出的错误:

Could not find an IRouter associated with the ActionContext. 

If your application is using endpoint routing then you can get a IUrlHelperFactory with dependency injection 
and use it to create a UrlHelper, or use Microsoft.AspNetCore.Routing.LinkGenerator.'

我不知道如何解决这个问题,因为我找不到任何方法来注入(inject) IUrlHelper。我不确定为什么需要这样做,因为无论如何它都不在 View 中。

字符串渲染方式:

public async Task<string> RenderToStringAsync(string viewName, object model)
{
    var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
    var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

    using (var sw = new StringWriter())
    {
        var viewResult = FindView(actionContext, viewName);

        if (viewResult == null)
        {
            throw new ArgumentNullException($"{viewName} does not match any available view");
        }

        var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
        {
            Model = model
        };

        var viewContext = new ViewContext(
                    actionContext,
                    viewResult,
                    viewDictionary,
                    new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
                    sw,
                    new HtmlHelperOptions());

        await viewResult.RenderAsync(viewContext); // Throws error <<<
        return sw.ToString();
    }
}

最佳答案

找到解决方案:

我认为,由于创建 ActionContext 并向其提供空 RouteData 对象会给我带来 IRouter 异常,因此下一个最佳解决方案是看看我是否可以仅使用实际请求中的 HttpContext。

因此,通过依赖项注入(inject),我添加了 _httpContextAccessor 并使用了可用的 HttpContext 对象。

为了完整起见,我将分享最终的实现:

将 View 呈现为 HTML:

RenderToString(字符串,对象);

var htmlBody = await Renderer.RenderToString($"/Views/Shared/confirm-email.cshtml", model);

服务:

public class ViewRenderService : IViewRenderService
{
    private readonly IRazorViewEngine _razorViewEngine;
    private readonly ITempDataProvider _tempDataProvider;
    private readonly IHttpContextAccessor _contextAccessor;

    public ViewRenderService(IRazorViewEngine razorViewEngine,
                             ITempDataProvider tempDataProvider,
                             IHttpContextAccessor contextAccessor)
    {
        _razorViewEngine = razorViewEngine;
        _tempDataProvider = tempDataProvider;
        _contextAccessor = contextAccessor;                                                                                            
    }

    public async Task<string> RenderToString(string viewName, object model)
    {
        var actionContext = new ActionContext(_contextAccessor.HttpContext, _contextAccessor.HttpContext.GetRouteData(), new ActionDescriptor());

        await using var sw = new StringWriter();
        var viewResult = FindView(actionContext, viewName);

        if (viewResult == null)
        {
            throw new ArgumentNullException($"{viewName} does not match any available view");
        }

        var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
        {
                Model = model
        };

        var viewContext = new ViewContext(
            actionContext,
            viewResult,
            viewDictionary,
            new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
            sw,
            new HtmlHelperOptions()
        );

        await viewResult.RenderAsync(viewContext);
        return sw.ToString();
    }

    private IView FindView(ActionContext actionContext, string viewName)
    {
        var getViewResult = _razorViewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true);
        if (getViewResult.Success)
        {
            return getViewResult.View;
        }

        var findViewResult = _razorViewEngine.FindView(actionContext, viewName, isMainPage: true);
        if (findViewResult.Success)
        {
            return findViewResult.View;
        }

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

        throw new InvalidOperationException(errorMessage);
    }
}

现在可以完美运行了。

关于c# - 在 core 3.0 中将 View 渲染为字符串 : Could not find an IRouter associated with the ActionContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59301912/

相关文章:

C#.net-|| OR 运算符不正常。似乎正在作为 AND

.net - 丢失版本和工具 : . NET Core、Core Tools、dotnet Core CLI,

在 Program.cs 中登录

c# - 从 Core 3 的类库中的渲染/处理的 Razor 页面中将 html 作为字符串返回

javascript - 无法从 RCL 引用静态文件

xml - net core 3 AddXmlSerializerFormatters() 配置选项

C# 随机颜色生成器无法正常工作

c# - 如何知道从 C++ dll 传递到 C# 的数组的大小

c# - C# 和 Java 之间始终相等的简单哈希

c# - UseExceptionHandler Blazor 服务器端