Asp.net core mvc 在ASP.NET核心中呈现包含字符串URL的Razor视图

Asp.net core mvc 在ASP.NET核心中呈现包含字符串URL的Razor视图,asp.net-core-mvc,Asp.net Core Mvc,我有一个Emailer类,我通过依赖注入发送电子邮件,该类获取要在电子邮件中发送的视图的内容。除非视图包含对底层URL帮助器的调用,例如使用如下所示的a标记,否则我的过程非常有效: <a asp-controller="Project" asp-action="List">Open</a> serviceCollection.AddSingleton<RazorViewToStringRenderer>(); 将此作为调用堆栈: at System.Thr

我有一个Emailer类,我通过依赖注入发送电子邮件,该类获取要在电子邮件中发送的视图的内容。除非视图包含对底层URL帮助器的调用,例如使用如下所示的a标记,否则我的过程非常有效:

<a asp-controller="Project" asp-action="List">Open</a>
serviceCollection.AddSingleton<RazorViewToStringRenderer>();
将此作为调用堆栈:

at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.Collections.Generic.List`1.get_Item(Int32 index)
at Microsoft.AspNetCore.Mvc.Routing.UrlHelper.get_Router()
at Microsoft.AspNetCore.Mvc.Routing.UrlHelper.GetVirtualPathData(String routeName, RouteValueDictionary values)
at Microsoft.AspNetCore.Mvc.Routing.UrlHelper.Action(UrlActionContext actionContext)
at Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Action(IUrlHelper helper, String action, String controller, Object values, String protocol, String host, String fragment)
at Microsoft.AspNetCore.Mvc.ViewFeatures.DefaultHtmlGenerator.GenerateActionLink(ViewContext viewContext, String linkText, String actionName, String controllerName, String protocol, String hostname, String fragment, Object routeValues, Object htmlAttributes)
at Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper.Process(TagHelperContext context, TagHelperOutput output)
at Microsoft.AspNetCore.Razor.TagHelpers.TagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>d__0.MoveNext()
serviceCollection.AddSingleton<RazorViewToStringRenderer>();
at System.ThrowHelper.throwargumentotofrangeexception(exception参数,exception资源)
位于System.Collections.Generic.List`1.get_项(Int32索引)
在Microsoft.AspNetCore.Mvc.Routing.UrlHelper.get_Router()上
位于Microsoft.AspNetCore.Mvc.Routing.UrlHelper.GetVirtualPathData(字符串routeName、RouteValueDictionary值)
位于Microsoft.AspNetCore.Mvc.Routing.UrlHelper.Action(UrlActionContext-actionContext)
位于Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Action(IUrlHelper helper、字符串操作、字符串控制器、对象值、字符串协议、字符串主机、字符串片段)
位于Microsoft.AspNetCore.Mvc.ViewFeatures.DefaultHtmlGenerator.GenerateActionLink(ViewContext ViewContext、String linkText、String actionName、String controllerName、String protocol、String hostname、String fragment、Object RouteValue、Object HtmlatAttribute)
位于Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper.Process(TagHelperContext上下文,TagHelperOutput输出)
位于Microsoft.AspNetCore.Razor.TagHelpers.TagHelper.ProcessAsync(TagHelperContext上下文,TagHelperOutput输出)
在Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.d_u0.MoveNext()中

我如何在不必对A元素或电子邮件内容进行硬编码的情况下解决这个问题?

我没有直接的答案,因为我有完全相同的问题。但我昨天提出了一个Github问题

serviceCollection.AddSingleton<RazorViewToStringRenderer>();

编辑:

我能够让它工作。调用堆栈提到找不到路由器,因此需要提供路由器:

serviceCollection.AddSingleton<RazorViewToStringRenderer>();
首先,我将其作为DI对象添加到构造函数参数中:

IHttpContextAccessor accessor
serviceCollection.AddSingleton<RazorViewToStringRenderer>();
在构造函数中:

_context = accessor.HttpContext;
serviceCollection.AddSingleton<RazorViewToStringRenderer>();
然后我将函数更改为:

private string renderViewAsString<TModel>(string folder, string viewName, TModel model)
{
  var actionContext = new ActionContext(_context, new RouteData(), new ActionDescriptor());
  var viewEngineResult = _viewEngine.FindView(actionContext, folder + "/" + viewName, false);
  var view = viewEngineResult.View;

  var viewData = new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary());
  viewData.Model = model;

  var tempData = new TempDataDictionary(_context, _tempDataProvider);

  using (var output = new StringWriter())
  {
    var viewContext = new ViewContext(actionContext, view, viewData, tempData, output, new HtmlHelperOptions());
    viewContext.RouteData = _context.GetRouteData();   //set route data here

    var task = view.RenderAsync(viewContext);
    task.Wait();

    return output.ToString();
  }
}
serviceCollection.AddSingleton<RazorViewToStringRenderer>();
private string renderViewAsString(字符串文件夹、字符串视图名、TModel模型)
{
var actionContext=new actionContext(_context,new RouteData(),new ActionDescriptor());
var viewEngineResult=_viewEngine.FindView(actionContext,文件夹+“/”+viewName,false);
var view=viewEngineResult.view;
var viewData=new ViewDataDictionary(new EmptyModelMetadataProvider(),new ModelStateDictionary());
viewData.Model=Model;
var tempData=new TempDataDictionary(_context,_tempDataProvider);
使用(var输出=新的StringWriter())
{
var viewContext=newviewcontext(actionContext、view、viewData、tempData、output、newhtmlhelpropoptions());
viewContext.RouteData=_context.GetRouteData();//在此处设置路由数据
var task=view.RenderAsync(viewContext);
task.Wait();
返回output.ToString();
}
}

这是我在ASP.NET Core 2.0中使用的

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;

namespace Website
{
    public class RazorViewToStringRenderer
    {
        private readonly IHttpContextAccessor accessor;
        private readonly IRazorViewEngine viewEngine;
        private readonly IServiceProvider serviceProvider;
        private readonly ITempDataProvider tempDataProvider;

        public RazorViewToStringRenderer(
            IHttpContextAccessor accessor, 
            IRazorViewEngine viewEngine, 
            IServiceProvider serviceProvider, 
            ITempDataProvider tempDataProvider)
        {
            this.accessor = accessor;
            this.viewEngine = viewEngine;
            this.serviceProvider = serviceProvider;
            this.tempDataProvider = tempDataProvider;
        }

        public string RenderViewToString<TModel>(string viewLocation, TModel model)
        {
            HttpContext httpContext = accessor.HttpContext;

            httpContext.RequestServices = serviceProvider;

            ActionContext actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

            IView view = FindView(actionContext, viewLocation);

            using (StringWriter stringWriter = new StringWriter())
            {
                ViewDataDictionary<TModel> viewDataDictionary = new ViewDataDictionary<TModel>(
                    new EmptyModelMetadataProvider(), 
                    new ModelStateDictionary());

                viewDataDictionary.Model = model;

                TempDataDictionary tempDataDictionary = new TempDataDictionary(
                    actionContext.HttpContext,
                    tempDataProvider);

                HtmlHelperOptions htmlHelperOptions = new HtmlHelperOptions();

                ViewContext viewContext = new ViewContext(
                    actionContext,
                    view,
                    viewDataDictionary,
                    tempDataDictionary,
                    stringWriter,
                    htmlHelperOptions);

                viewContext.RouteData = accessor.HttpContext.GetRouteData();

                view.RenderAsync(viewContext).Wait();

                return stringWriter.ToString();
            }
        }

        private IView FindView(ActionContext actionContext, string viewLocation)
        {
            ViewEngineResult getViewResult = viewEngine.GetView(null,  viewLocation,  true);

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

            ViewEngineResult findViewResult = viewEngine.FindView(actionContext, viewLocation, true);

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

            IEnumerable<string> searchedLocations = getViewResult.SearchedLocations.Concat(findViewResult.SearchedLocations);

            string message = string.Join(
                Environment.NewLine,
                new[] { $"Unable to find view '{viewLocation}'. The following locations were searched:" }.Concat(searchedLocations)); ;

            throw new Exception(message);
        }
    }
}
serviceCollection.AddSingleton<RazorViewToStringRenderer>();
使用系统;
使用System.Collections.Generic;
使用System.IO;
使用System.Linq;
使用Microsoft.AspNetCore.Http;
使用Microsoft.AspNetCore.Mvc;
使用Microsoft.AspNetCore.Mvc.Abstractions;
使用Microsoft.AspNetCore.Mvc.ModelBinding;
使用Microsoft.AspNetCore.Mvc.Razor;
使用Microsoft.AspNetCore.Mvc.Rendering;
使用Microsoft.AspNetCore.Mvc.ViewEngines;
使用Microsoft.AspNetCore.Mvc.ViewFeatures;
使用Microsoft.AspNetCore.Routing;
名称空间网站
{
公共级RazorViewToStringrender
{
专用只读IHttpContextAccessor访问器;
专用只读IRazorViewEngine视图引擎;
私有只读服务器ViceProvider服务提供商;
私有只读ITempDataProvider tempDataProvider;
公共RazorViewToStringrender(
IHttpContextAccessor访问器,
IRazorViewEngine视图引擎,
IServiceProvider服务提供商,
ITempDataProvider(临时数据提供程序)
{
this.accessor=访问器;
this.viewEngine=viewEngine;
this.serviceProvider=serviceProvider;
this.tempDataProvider=tempDataProvider;
}
公共字符串RenderViewToString(字符串视图位置,TModel模型)
{
HttpContext HttpContext=accessor.HttpContext;
httpContext.RequestServices=serviceProvider;
ActionContext ActionContext=new ActionContext(httpContext,new RoutedData(),new ActionDescriptor());
IView view=FindView(actionContext,viewLocation);
使用(StringWriter StringWriter=new StringWriter())
{
ViewDataDictionary ViewDataDictionary=新建ViewDataDictionary(
新建EmptyModelMetadataProvider(),
新ModelStateDictionary());
viewDataDictionary.Model=Model;
TempDataDictionary TempDataDictionary=新建TempDataDictionary(
actionContext.HttpContext,
临时数据提供者);
HtmlHelperOptions HtmlHelperOptions=新HtmlHelperOptions();
ViewContext ViewContext=新建ViewContext(
行动背景,
看法
viewDataDictionary,
临时数据字典,
剧作家,
HtmlHelpOptions);
viewContext.RouteData=accessor.HttpContext.GetRouteData();
view.RenderAsync(viewContext.Wait();
返回stringWriter.ToString();
}
}
private IView FindView(ActionContext ActionContext,string viewLocation)
{
ViewEngineResult getViewResult=viewEngine.GetView(null,viewLocation,true);
if(getViewResult.Success)
{
返回getViewResult.View;
}
ViewEngineResult findViewResult=viewEngine.FindView(actionContext,viewLocation,true);
如果(findViewResult.Success)
{