【问题标题】:How to render an ASP.NET MVC view as a string?如何将 ASP.NET MVC 视图呈现为字符串?
【发布时间】:2016-10-10 00:08:59
【问题描述】:

我想输出两个不同的视图(一个作为将作为电子邮件发送的字符串),另一个是显示给用户的页面。

这在 ASP.NET MVC beta 中是否可行?

我尝试了多个示例:

1. RenderPartial to String in ASP.NET MVC Beta

如果我使用此示例,我会收到“HTTP 后无法重定向 标头已发送。”。

2. MVC Framework: Capturing the output of a view

如果我使用它,我似乎无法执行 redirectToAction,因为它 试图渲染一个可能不存在的视图。如果我确实返回视图,它 完全一团糟,看起来一点也不对。

是否有人对我遇到的这些问题有任何想法/解决方案,或者对更好的问题有任何建议?

非常感谢!

下面是一个例子。我要做的是创建 GetViewForEmail 方法

public ActionResult OrderResult(string ref)
{
    //Get the order
    Order order = OrderService.GetOrder(ref);

    //The email helper would do the meat and veg by getting the view as a string
    //Pass the control name (OrderResultEmail) and the model (order)
    string emailView = GetViewForEmail("OrderResultEmail", order);

    //Email the order out
    EmailHelper(order, emailView);
    return View("OrderResult", order);
}

接受了 Tim Scott 的回答(我稍作修改和格式化):

public virtual string RenderViewToString(
    ControllerContext controllerContext,
    string viewPath,
    string masterPath,
    ViewDataDictionary viewData,
    TempDataDictionary tempData)
{
    Stream filter = null;
    ViewPage viewPage = new ViewPage();

    //Right, create our view
    viewPage.ViewContext = new ViewContext(controllerContext, new WebFormView(viewPath, masterPath), viewData, tempData);

    //Get the response context, flush it and get the response filter.
    var response = viewPage.ViewContext.HttpContext.Response;
    response.Flush();
    var oldFilter = response.Filter;

    try
    {
        //Put a new filter into the response
        filter = new MemoryStream();
        response.Filter = filter;

        //Now render the view into the memorystream and flush the response
        viewPage.ViewContext.View.Render(viewPage.ViewContext, viewPage.ViewContext.HttpContext.Response.Output);
        response.Flush();

        //Now read the rendered view.
        filter.Position = 0;
        var reader = new StreamReader(filter, response.ContentEncoding);
        return reader.ReadToEnd();
    }
    finally
    {
        //Clean up.
        if (filter != null)
        {
            filter.Dispose();
        }

        //Now replace the response filter
        response.Filter = oldFilter;
    }
}

使用示例

假设控制器调用以获取订单确认电子邮件,传递 Site.Master 位置。

string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);

【问题讨论】:

  • 如何将它与强类型视图一起使用? IE。如何将模型提供给页面?
  • 不能使用这个并在之后创建 JsonResult,因为在 headers 发送后无法设置内容类型(因为 Flush 发送它们)。
  • 因为没有唯一的正确答案,我想。 :) 我创建了一个特定于我的问题,但我知道它也会被广泛问到。
  • 建议的解决方案在 MVC 3 中不起作用。
  • @Qua:建议的解决方案已有两年多的历史了。我也不希望它适用于 MVC 3!此外,现在有更好的方法来做到这一点。

标签: asp.net-mvc rendering


【解决方案1】:

这是我想出的,它对我有用。我将以下方法添加到我的控制器基类中。 (您总是可以在其他地方创建这些静态方法,我想这些方法可以接受控制器作为参数)

MVC2 .ascx 样式

protected string RenderViewToString<T>(string viewPath, T model) {
  ViewData.Model = model;
  using (var writer = new StringWriter()) {
    var view = new WebFormView(ControllerContext, viewPath);
    var vdd = new ViewDataDictionary<T>(model);
    var viewCxt = new ViewContext(ControllerContext, view, vdd,
                                new TempDataDictionary(), writer);
    viewCxt.View.Render(viewCxt, writer);
    return writer.ToString();
  }
}

Razor .cshtml 样式

public string RenderRazorViewToString(string viewName, object model)
{
  ViewData.Model = model;
  using (var sw = new StringWriter())
  {
    var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,
                                                             viewName);
    var viewContext = new ViewContext(ControllerContext, viewResult.View,
                                 ViewData, TempData, sw);
    viewResult.View.Render(viewContext, sw);
    viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
    return sw.GetStringBuilder().ToString();
  }
}

编辑:添加 Razor 代码。

【讨论】:

  • 将视图渲染为字符串总是“与整个路由概念不一致”,因为它与路由无关。我不确定为什么一个有效的答案会被否决。
  • 我认为你可能需要从 Razor 版本的方法声明中删除“静态”,否则它找不到 ControllerContext 等。
  • 您需要为那些多余的空格实现自己的删除方法。我能想到的最好的方法是将字符串加载到 XmlDocument 中,然后按照我在上一条评论中留下的链接将其写回带有 XmlWriter 的字符串。我真的希望这会有所帮助。
  • 嗯,我应该如何使用 WebApi 控制器来做到这一点,任何建议将不胜感激
  • 大家好,所有控制器都使用“Static”关键字以使其通用,您必须创建静态类,并且在其中您必须将此方法与“this”作为参数放入“ControllerContext” .你可以在这里看到stackoverflow.com/a/18978036/2318354它。
【解决方案2】:

这个答案不在我的路上。这最初来自https://stackoverflow.com/a/2759898/2318354,但在这里我展示了将它与“静态”关键字一起使用以使其对所有控制器通用的方法。

为此,您必须在类文件中创建 static 类。 (假设你的类文件名是 Utils.cs )

此示例适用于 Razor。

Utils.cs

public static class RazorViewToString
{
    public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
    {
        controller.ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
            var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }
}

现在您可以通过将“this”作为参数传递给控制器​​,在控制器文件中添加命名空间来从控制器调用此类。

string result = RazorViewToString.RenderRazorViewToString(this ,"ViewName", model);

根据@Sergey 的建议,这个扩展方法也可以从控制器调用,如下所示

string result = this.RenderRazorViewToString("ViewName", model);

我希望这对您使代码干净整洁。

【讨论】:

  • 不错的解决方案!有一点,RenderRazorViewToString实际上是扩展方法(因为你用this关键字传递控制器参数),所以这个扩展方法可以这样调用:this.RenderRazorViewToString("ViewName", model);
  • @Sergey Hmmm... 让我检查一下,如果没问题,我会更新我的答案。无论如何感谢您的建议。
  • Dilip0165,我在 var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName); 上得到一个空引用错误。你有什么想法吗?
  • @CB4 我认为这可能是您传递给函数的“viewName”问题。您必须根据您的文件夹结构传递带有完整路径的“viewName”。所以看看这个东西。
  • @Sergey 感谢您的建议,我已根据您的建议更新了我的答案,这是完全正确的
【解决方案3】:

这对我有用:

public virtual string RenderView(ViewContext viewContext)
{
    var response = viewContext.HttpContext.Response;
    response.Flush();
    var oldFilter = response.Filter;
    Stream filter = null;
    try
    {
        filter = new MemoryStream();
        response.Filter = filter;
        viewContext.View.Render(viewContext, viewContext.HttpContext.Response.Output);
        response.Flush();
        filter.Position = 0;
        var reader = new StreamReader(filter, response.ContentEncoding);
        return reader.ReadToEnd();
    }
    finally
    {
        if (filter != null)
        {
            filter.Dispose();
        }
        response.Filter = oldFilter;
    }
}

【讨论】:

  • 感谢您的评论,但这不是用于在视图中渲染吗?我如何在我更新问题的上下文中使用它?
  • 抱歉,我还在想去年的 Silverlight,它的第一个 rc 为 0。:) 我今天要试一试。 (只要我制定出正确的视图路径格式)
  • 这仍然会破坏 RC1 中的重定向
  • 击败:不,它没有。如果是这样,那么你做错了什么。
  • 将此与stackoverflow.com/questions/520863/… 合并,增加了对 ViewEnginesCollection 的认识,尝试弹出部分视图并得到此stackoverflow.com/questions/520863/…。 :E
【解决方案4】:

我找到了一种新的解决方案,它可以将视图呈现为字符串,而不必弄乱当前 HttpContext 的响应流(它不允许您更改响应的 ContentType 或其他标头)。

基本上,你要做的就是创建一个假的 HttpContext 来让视图自己呈现:

/// <summary>Renders a view to string.</summary>
public static string RenderViewToString(this Controller controller,
                                        string viewName, object viewData) {
    //Create memory writer
    var sb = new StringBuilder();
    var memWriter = new StringWriter(sb);

    //Create fake http context to render the view
    var fakeResponse = new HttpResponse(memWriter);
    var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
    var fakeControllerContext = new ControllerContext(
        new HttpContextWrapper(fakeContext),
        controller.ControllerContext.RouteData,
        controller.ControllerContext.Controller);

    var oldContext = HttpContext.Current;
    HttpContext.Current = fakeContext;

    //Use HtmlHelper to render partial view to fake context
    var html = new HtmlHelper(new ViewContext(fakeControllerContext,
        new FakeView(), new ViewDataDictionary(), new TempDataDictionary()),
        new ViewPage());
    html.RenderPartial(viewName, viewData);

    //Restore context
    HttpContext.Current = oldContext;    

    //Flush memory and return output
    memWriter.Flush();
    return sb.ToString();
}

/// <summary>Fake IView implementation used to instantiate an HtmlHelper.</summary>
public class FakeView : IView {
    #region IView Members

    public void Render(ViewContext viewContext, System.IO.TextWriter writer) {
        throw new NotImplementedException();
    }

    #endregion
}

这适用于 ASP.NET MVC 1.0,以及 ContentResult、JsonResult 等(更改原始 HttpResponse 上的标头不会抛出“服务器无法在发送 HTTP 标头后设置内容类型" 例外)。

更新:在 ASP.NET MVC 2.0 RC 中,代码发生了一些变化,因为我们必须传入用于将视图写入 ViewContextStringWriter

//...

//Use HtmlHelper to render partial view to fake context
var html = new HtmlHelper(
    new ViewContext(fakeControllerContext, new FakeView(),
        new ViewDataDictionary(), new TempDataDictionary(), memWriter),
    new ViewPage());
html.RenderPartial(viewName, viewData);

//...

【讨论】:

  • HtmlHelper 对象上没有 RenderPartial 方法。这是不可能的 - html.RenderPartial(viewName, viewData);
  • 在 ASP.NET MVC 1.0 版中有几个 RenderPartial 扩展方法。我特别使用的是 System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(this HtmlHelper, string, object)。我不知道该方法是否已添加到 MVC 的最新版本中,并且在早期版本中不存在。
  • 谢谢。只需将 System.Web.Mvc.Html 命名空间添加到 using 声明(否则 html.RenderPartial(..) 当然无法访问:))
  • 有人用 MVC2 的 RC 做这个吗?他们向 ViewContext 添加了一个额外的 Textwriter 参数。我尝试只添加一个新的 StringWriter(),但它不起作用。
  • @bekelmw:我更新了回复。您必须传入您用来写入StringBuilder 的原始StringWriter,而不是新实例,否则视图的输出将丢失。
【解决方案5】:

This article描述了不同场景下如何将View渲染成字符串:

  1. MVC 控制器调用另一个自己的 ActionMethods
  2. MVC 控制器调用另一个 MVC 控制器的 ActionMethod
  3. WebAPI 控制器调用 MVC 控制器的 ActionMethod

解决方案/代码作为一个名为 ViewRenderer 的类提供。它是 Rick Stahl 的WestwindToolkit at GitHub 的一部分。

用法(3. - WebAPI 示例):

string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));

【讨论】:

  • 也作为 NuGet 包 West Wind Web MVC Utilities (nuget.org/packages/Westwind.Web.Mvc)。作为奖励,视图渲染器不仅可以渲染部分视图,还可以渲染包括布局在内的整个视图。带代码的博客文章:weblog.west-wind.com/posts/2012/May/30/…
  • 如果能分成更小的包就好了。 Nuget 包对您的 web.config 进行了一系列更改,并将 js 文件添加到您的项目中,然后在您卸载它时不会清理这些文件:/
  • 唉,所有这些都适用于有主机的情况......不适合像单元测试这样想要在没有任何 IIS 或其他的情况下呈现视图(使用来自固定模型的数据)的东西主机或浏览器,或其他上下文。
【解决方案6】:

如果你想完全放弃 MVC,从而避免所有 HttpContext 混乱......

using RazorEngine;
using RazorEngine.Templating; // For extension methods.

string razorText = System.IO.File.ReadAllText(razorTemplateFileLocation);
string emailBody = Engine.Razor.RunCompile(razorText, "templateKey", typeof(Model), model);

这里使用了很棒的开源 Razor 引擎: https://github.com/Antaris/RazorEngine

【讨论】:

  • 不错!您知道是否有类似的 WebForms 语法解析引擎?我仍然有一些旧的 WebForms 视图还不能移动到 Razor。
  • 您好,我在使用 razorengine 时遇到了很多问题,并且错误报告不是很好。我认为不支持 Url 助手
  • @Layinka 不确定这是否有帮助,但大多数错误信息都在异常的CompilerErrors 属性中。
【解决方案7】:

ASP NET CORE 的附加提示:

界面:

public interface IViewRenderer
{
  Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model);
}

实施:

public class ViewRenderer : IViewRenderer
{
  private readonly IRazorViewEngine viewEngine;

  public ViewRenderer(IRazorViewEngine viewEngine) => this.viewEngine = viewEngine;

  public async Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model)
  {
    ViewEngineResult viewEngineResult = this.viewEngine.FindView(controller.ControllerContext, name, false);

    if (!viewEngineResult.Success)
    {
      throw new InvalidOperationException(string.Format("Could not find view: {0}", name));
    }

    IView view = viewEngineResult.View;
    controller.ViewData.Model = model;

    await using var writer = new StringWriter();
    var viewContext = new ViewContext(
       controller.ControllerContext,
       view,
       controller.ViewData,
       controller.TempData,
       writer,
       new HtmlHelperOptions());

       await view.RenderAsync(viewContext);

       return writer.ToString();
  }
}

Startup.cs注册

...
 services.AddSingleton<IViewRenderer, ViewRenderer>();
...

以及在控制器中的使用:

public MyController: Controller
{
  private readonly IViewRenderer renderer;
  public MyController(IViewRendere renderer) => this.renderer = renderer;
  public async Task<IActionResult> MyViewTest
  {
    var view = await this.renderer.RenderAsync(this, "MyView", model);
    return new OkObjectResult(view);
  }
}

【讨论】:

    【解决方案8】:

    要在服务层中将视图呈现为字符串,而不必传递 ControllerContext,这里有一篇很好的 Rick Strahl 文章http://www.codemag.com/Article/1312081,它创建了一个通用控制器。代码摘要如下:

    // Some Static Class
    public static string RenderViewToString(ControllerContext context, string viewPath, object model = null, bool partial = false)
    {
        // first find the ViewEngine for this view
        ViewEngineResult viewEngineResult = null;
        if (partial)
            viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
        else
            viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
    
        if (viewEngineResult == null)
            throw new FileNotFoundException("View cannot be found.");
    
        // get the view and attach the model to view data
        var view = viewEngineResult.View;
        context.Controller.ViewData.Model = model;
    
        string result = null;
    
        using (var sw = new StringWriter())
        {
            var ctx = new ViewContext(context, view, context.Controller.ViewData, context.Controller.TempData, sw);
            view.Render(ctx, sw);
            result = sw.ToString();
        }
    
        return result;
    }
    
    // In the Service Class
    public class GenericController : Controller
    { }
    
    public static T CreateController<T>(RouteData routeData = null) where T : Controller, new()
    {
        // create a disconnected controller instance
        T controller = new T();
    
        // get context wrapper from HttpContext if available
        HttpContextBase wrapper;
        if (System.Web.HttpContext.Current != null)
            wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
        else
            throw new InvalidOperationException("Cannot create Controller Context if no active HttpContext instance is available.");
    
        if (routeData == null)
            routeData = new RouteData();
    
        // add the controller routing if not existing
        if (!routeData.Values.ContainsKey("controller") &&
            !routeData.Values.ContainsKey("Controller"))
            routeData.Values.Add("controller", controller.GetType().Name.ToLower().Replace("controller", ""));
    
        controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
        return controller;
    }
    

    然后在Service类中渲染View:

    var stringView = RenderViewToString(CreateController<GenericController>().ControllerContext, "~/Path/To/View/Location/_viewName.cshtml", theViewModel, true);
    

    【讨论】:

    • ViewEngines 似乎不再正确 [.NET 5.0]
    • 此代码针对 .NET Framework,而不是 Core。
    【解决方案9】:

    您可以使用这种方式获取字符串中的视图

    protected string RenderPartialViewToString(string viewName, object model)
    {
        if (string.IsNullOrEmpty(viewName))
            viewName = ControllerContext.RouteData.GetRequiredString("action");
    
        if (model != null)
            ViewData.Model = model;
    
        using (StringWriter sw = new StringWriter())
        {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
    
            return sw.GetStringBuilder().ToString();
        }
    }
    

    我们以两种方式调用这个方法

    string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", null)
    

    var model = new Person()
    string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", model)
    

    【讨论】:

      【解决方案10】:

      我正在使用 MVC 1.0 RTM,但上述解决方案均不适合我。但是这个做到了:

      Public Function RenderView(ByVal viewContext As ViewContext) As String
      
          Dim html As String = ""
      
          Dim response As HttpResponse = HttpContext.Current.Response
      
          Using tempWriter As New System.IO.StringWriter()
      
              Dim privateMethod As MethodInfo = response.GetType().GetMethod("SwitchWriter", BindingFlags.NonPublic Or BindingFlags.Instance)
      
              Dim currentWriter As Object = privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {tempWriter}, Nothing)
      
              Try
                  viewContext.View.Render(viewContext, Nothing)
                  html = tempWriter.ToString()
              Finally
                  privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {currentWriter}, Nothing)
              End Try
      
          End Using
      
          Return html
      
      End Function
      

      【讨论】:

        【解决方案11】:

        我从另一个网站看到了 MVC 3 和 Razor 的实现,它对我有用:

            public static string RazorRender(Controller context, string DefaultAction)
            {
                string Cache = string.Empty;
                System.Text.StringBuilder sb = new System.Text.StringBuilder();
                System.IO.TextWriter tw = new System.IO.StringWriter(sb); 
        
                RazorView view_ = new RazorView(context.ControllerContext, DefaultAction, null, false, null);
                view_.Render(new ViewContext(context.ControllerContext, view_, new ViewDataDictionary(), new TempDataDictionary(), tw), tw);
        
                Cache = sb.ToString(); 
        
                return Cache;
        
            } 
        
            public static string RenderRazorViewToString(string viewName, object model)
            {
        
                ViewData.Model = model;
                using (var sw = new StringWriter())
                {
                    var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
                    var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
                    viewResult.View.Render(viewContext, sw);
                    return sw.GetStringBuilder().ToString();
                }
            } 
        
            public static class HtmlHelperExtensions
            {
                public static string RenderPartialToString(ControllerContext context, string partialViewName, ViewDataDictionary viewData, TempDataDictionary tempData)
                {
                    ViewEngineResult result = ViewEngines.Engines.FindPartialView(context, partialViewName);
        
                    if (result.View != null)
                    {
                        StringBuilder sb = new StringBuilder();
                        using (StringWriter sw = new StringWriter(sb))
                        {
                            using (HtmlTextWriter output = new HtmlTextWriter(sw))
                            {
                                ViewContext viewContext = new ViewContext(context, result.View, viewData, tempData, output);
                                result.View.Render(viewContext, output);
                            }
                        }
                        return sb.ToString();
                    } 
        
                    return String.Empty;
        
                }
        
            }
        

        更多关于Razor render- MVC3 View Render to String

        【讨论】:

        • 是的,这实际上或多或少是已接受答案的副本。 :)
        【解决方案12】:

        快速提示

        对于强类型模型,只需将其添加到 ViewData.Model 属性,然后再传递给 RenderViewToString。例如

        this.ViewData.Model = new OrderResultEmailViewModel(order);
        string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
        

        【讨论】:

          【解决方案13】:

          要重复一个更未知的问题,请查看MvcIntegrationTestFramework

          它可以节省您编写自己的助手来流式传输结果,并且被证明可以很好地工作。我假设这将在一个测试项目中,作为奖励,一旦你有了这个设置,你就会拥有其他测试功能。主要麻烦可能是整理依赖链。

           private static readonly string mvcAppPath = 
               Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory 
               + "\\..\\..\\..\\MyMvcApplication");
           private readonly AppHost appHost = new AppHost(mvcAppPath);
          
              [Test]
              public void Root_Url_Renders_Index_View()
              {
                  appHost.SimulateBrowsingSession(browsingSession => {
                      RequestResult result = browsingSession.ProcessRequest("");
                      Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));
                  });
          }
          

          【讨论】:

            【解决方案14】:

            这是我为 ASP.NETCore RC2 编写的一个类。我使用它是为了使用 Razor 生成 html 电子邮件。

            using Microsoft.AspNetCore.Http;
            using Microsoft.AspNetCore.Mvc;
            using Microsoft.AspNetCore.Mvc.Abstractions;
            using Microsoft.AspNetCore.Mvc.ModelBinding;
            using Microsoft.AspNetCore.Mvc.Rendering;
            using Microsoft.AspNetCore.Mvc.ViewEngines;
            using Microsoft.AspNetCore.Mvc.ViewFeatures;
            using Microsoft.AspNetCore.Routing;
            using System.IO;
            using System.Threading.Tasks;
            
            namespace cloudscribe.Web.Common.Razor
            {
                /// <summary>
                /// the goal of this class is to provide an easy way to produce an html string using 
                /// Razor templates and models, for use in generating html email.
                /// </summary>
                public class ViewRenderer
                {
                    public ViewRenderer(
                        ICompositeViewEngine viewEngine,
                        ITempDataProvider tempDataProvider,
                        IHttpContextAccessor contextAccesor)
                    {
                        this.viewEngine = viewEngine;
                        this.tempDataProvider = tempDataProvider;
                        this.contextAccesor = contextAccesor;
                    }
            
                    private ICompositeViewEngine viewEngine;
                    private ITempDataProvider tempDataProvider;
                    private IHttpContextAccessor contextAccesor;
            
                    public async Task<string> RenderViewAsString<TModel>(string viewName, TModel model)
                    {
            
                        var viewData = new ViewDataDictionary<TModel>(
                                    metadataProvider: new EmptyModelMetadataProvider(),
                                    modelState: new ModelStateDictionary())
                        {
                            Model = model
                        };
            
                        var actionContext = new ActionContext(contextAccesor.HttpContext, new RouteData(), new ActionDescriptor());
                        var tempData = new TempDataDictionary(contextAccesor.HttpContext, tempDataProvider);
            
                        using (StringWriter output = new StringWriter())
                        {
            
                            ViewEngineResult viewResult = viewEngine.FindView(actionContext, viewName, true);
            
                            ViewContext viewContext = new ViewContext(
                                actionContext,
                                viewResult.View,
                                viewData,
                                tempData,
                                output,
                                new HtmlHelperOptions()
                            );
            
                            await viewResult.View.RenderAsync(viewContext);
            
                            return output.GetStringBuilder().ToString();
                        }
                    }
                }
            }
            

            【讨论】:

              【解决方案15】:

              当我使用上述方法出错时,我找到了一种更好的方法来呈现剃刀视图页面,这个解决方案适用于 web 表单环境和 mvc 环境。 不需要控制器。

              这是代码示例,在这个示例中,我使用异步 http 处理程序模拟了一个 mvc 操作:

                  /// <summary>
                  /// Enables processing of HTTP Web requests asynchronously by a custom HttpHandler that implements the IHttpHandler interface.
                  /// </summary>
                  /// <param name="context">An HttpContext object that provides references to the intrinsic server objects.</param>
                  /// <returns>The task to complete the http request.</returns>
                  protected override async Task ProcessRequestAsync(HttpContext context)
                  {
                      if (this._view == null)
                      {
                          this.OnError(context, new FileNotFoundException("Can not find the mvc view file.".Localize()));
                          return;
                      }
                      object model = await this.LoadModelAsync(context);
                      WebPageBase page = WebPageBase.CreateInstanceFromVirtualPath(this._view.VirtualPath);
                      using (StringWriter sw = new StringWriter())
                      {
                          page.ExecutePageHierarchy(new WebPageContext(new HttpContextWrapper(context), page, model), sw);
                          await context.Response.Output.WriteAsync(sw.GetStringBuilder().ToString());
                      }
                  }
              

              【讨论】:

                【解决方案16】:

                对我来说最简单的方法是:

                  public string GetFileAsString(string path)
                  {
                        var html = "";                        
                
                        FileStream fileStream = new FileStream(path, FileMode.Open);
                
                        using (StreamReader reader = new StreamReader(fileStream))
                        {
                            html += reader.ReadLine();
                        }
                
                        return html;
                   }
                

                我将它用于电子邮件并确保文件仅包含 CSS 和 HTML

                【讨论】:

                • 不错的解决方案,但它与 MVC 无关,实际上并没有使用它。因此,对于任何想要呈现“真实”视图的人来说,这不是一个合适的答案。
                • 感谢您的反馈,我知道它与这个特定问题无关,但我只是在这里回复了一种更通用的方法,供任何人寻找将任何文件转换为字符串的方法而无需花费渲染。
                猜你喜欢
                • 2011-01-28
                • 2013-09-28
                • 2012-03-03
                • 1970-01-01
                • 2011-06-23
                相关资源
                最近更新 更多