简体   繁体   English

如何将 ASP.NET MVC 视图呈现为字符串?

[英]How to render an ASP.NET MVC view as a string?

I want to output two different views (one as a string that will be sent as an email), and the other the page displayed to a user.我想输出两个不同的视图(一个作为将作为电子邮件发送的字符串),另一个是向用户显示的页面。

Is this possible in ASP.NET MVC beta?这在 ASP.NET MVC 测试版中可能吗?

I've tried multiple examples:我尝试了多个示例:

1. RenderPartial to String in ASP.NET MVC Beta 1. ASP.NET MVC Beta 中的 RenderPartial to String

If I use this example, I receive the "Cannot redirect after HTTP headers have been sent.".如果我使用这个例子,我会收到“发送 HTTP 标头后无法重定向。”。

2. MVC Framework: Capturing the output of a view 2. MVC 框架:捕获视图的输出

If I use this, I seem to be unable to do a redirectToAction, as it tries to render a view that may not exist.如果我使用它,我似乎无法执行redirectToAction,因为它试图呈现可能不存在的视图。 If I do return the view, it is completely messed up and doesn't look right at all.如果我确实返回视图,它会完全混乱并且看起来根本不正确。

Does anyone have any ideas/solutions to these issues i have, or have any suggestions for better ones?有没有人对我遇到的这些问题有任何想法/解决方案,或者有更好的建议?

Many thanks!非常感谢!

Below is an example.下面是一个例子。 What I'm trying to do is create the GetViewForEmail method :我想要做的是创建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);
}

Accepted answer from Tim Scott (changed and formatted a little by me):接受 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;
    }
}

Example usage示例用法

Assuming a call from the controller to get the order confirmation email, passing the Site.Master location.假设来自控制器的调用以获取订单确认电子邮件,传递 Site.Master 位置。

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

Here's what I came up with, and it's working for me. 这是我想出的,对我有用。 I added the following method(s) to my controller base class. 我在控制器基类中添加了以下方法。 (You can always make these static methods somewhere else that accept a controller as a parameter I suppose) (您总是可以在其他地方使这些静态方法成为我想接受控制器作为参数的位置)

MVC2 .ascx style 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 style 剃刀.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();
  }
}

Edit: added Razor code. 编辑:添加了剃刀代码。

This answer is not on my way . 这个答案不在我身边。 This is originally from https://stackoverflow.com/a/2759898/2318354 but here I have show the way to use it with "Static" Keyword to make it common for all Controllers . 这最初来自https://stackoverflow.com/a/2759898/2318354,但在这里,我展示了将其与“静态”关键字一起使用以使其对所有Controller通用的方法。

For that you have to make static class in class file . 为此,您必须在类文件中创建static类。 (Suppose your Class File Name is Utils.cs ) (假设您的类文件名是Utils.cs)

This example is For 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();
        }
    }
}

Now you can call this class from your controller by adding NameSpace in your Controller File as following way by passing "this" as parameter to Controller. 现在,您可以通过将“ this”作为参数传递给Controller的方式,通过在Controller File中添加NameSpace的方式从控制器中调用此类。

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

As suggestion given by @Sergey this extension method can also call from cotroller as given below 根据@Sergey的建议,此扩展方法也可以从cotroller调用,如下所示

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

I hope this will be useful to you make code clean and neat. 我希望这对您使代码整洁有帮助。

This works for me: 这对我有用:

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;
    }
}

I found a new solution that renders a view to string without having to mess with the Response stream of the current HttpContext (which doesn't allow you to change the response's ContentType or other headers). 我找到了一个新的解决方案,该视图可以将视图呈现为字符串,而不必弄乱当前HttpContext的Response流(不允许您更改响应的ContentType或其他标头)。

Basically, all you do is create a fake HttpContext for the view to render itself: 基本上,您要做的就是为视图创建一个伪造的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
}

This works on ASP.NET MVC 1.0, together with ContentResult, JsonResult, etc. (changing Headers on the original HttpResponse doesn't throw the " Server cannot set content type after HTTP headers have been sent " exception). 这可以在ASP.NET MVC 1.0上以及ContentResult,JsonResult等上使用。(在原始HttpResponse上更改标头不会引发“ 发送HTTP标头后服务器无法设置内容类型 ”异常)。

Update: in ASP.NET MVC 2.0 RC, the code changes a bit because we have to pass in the StringWriter used to write the view into the ViewContext : 更新:在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);

//...

This article describes how to render a View to a string in different scenarios: 本文介绍如何在不同情况下将视图呈现为字符串:

  1. MVC Controller calling another of its own ActionMethods MVC控制器调用其自己的另一个ActionMethods
  2. MVC Controller calling an ActionMethod of another MVC Controller MVC控制器调用另一个MVC控制器的ActionMethod
  3. WebAPI Controller calling an ActionMethod of an MVC Controller WebAPI控制器调用MVC控制器的ActionMethod

The solution/code is provided as a class called ViewRenderer . 解决方案/代码作为称为ViewRenderer的类提供。 It is part of Rick Stahl's WestwindToolkit at GitHub . 它是GitHub上 Rick Stahl的WestwindToolkit的一部分。

Usage (3. - WebAPI example): 用法 (3-WebAPI示例):

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

If you want to forgo MVC entirely, thereby avoiding all the HttpContext mess... 如果您想完全放弃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);

This uses the awesome open source Razor Engine here: https://github.com/Antaris/RazorEngine 它在这里使用了很棒的开源Razor Engine: https//github.com/Antaris/RazorEngine

you are get the view in string using this way 您将使用这种方式获取字符串视图

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();
    }
}

We are call this method in two way 我们以两种方式称呼此方法

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

OR 要么

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

I am using MVC 1.0 RTM and none of the above solutions worked for me. 我正在使用MVC 1.0 RTM,但以上解决方案均不适用于我。 But this one did: 但这确实做到了:

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

To render a view to a string in the Service Layer without having to pass ControllerContext around, there is a good Rick Strahl article here http://www.codemag.com/Article/1312081 that creates a generic controller. 为了在服务层中呈现字符串视图而不必传递ControllerContext,这里有一篇很好的Rick Strahl文章, 网址为http://www.codemag.com/Article/1312081 ,该文章创建了一个通用控制器。 Code summary below: 下面的代码摘要:

// 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;
}

Then to render the View in the Service class: 然后在Service类中呈现View:

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

I saw an implementation for MVC 3 and Razor from another website, it worked for me: 我从另一个网站上看到了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;

        }

    }

More on Razor render- MVC3 View Render to String 有关Razor渲染的更多信息-MVC3视图渲染为字符串

Additional tip for ASP NET CORE: ASP NET CORE的其他提示:

Interface: 接口:

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

Implementation: 实现方式:

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();
  }
}

Registration in Startup.cs Startup.cs注册

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

And usage in controller: 并在控制器中使用:

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);
  }
}

Quick tip 小建议

For a strongly typed Model just add it to the ViewData.Model property before passing to RenderViewToString. 对于强类型模型,只需将其添加到ViewData.Model属性,然后再传递给RenderViewToString。 eg 例如

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

To repeat from a more unknown question, take a look at MvcIntegrationTestFramework . 要重复一个更未知的问题,请看一下MvcIntegrationTestFramework

It makes saves you writing your own helpers to stream result and is proven to work well enough. 它使您省去编写自己的帮助程序以流式传输结果的麻烦,并且事实证明它可以很好地工作。 I'd assume this would be in a test project and as a bonus you would have the other testing capabilities once you've got this setup. 我假设这将在一个测试项目中,而且,一旦完成此设置,您将获得其他测试功能。 Main bother would probably be sorting out the dependency chain. 主要的麻烦可能是整理依赖链。

 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"));
        });
}

Here is a class I wrote to do this for ASP.NETCore RC2. 这是我为ASP.NETCore RC2为此目的编写的一个类。 I use it so I can generate html email using Razor. 我使用它,因此可以使用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();
            }
        }
    }
}

I found a better way to render razor view page when I got error with the methods above, this solution for both web form environment and mvc environment. 当上述方法出现错误时,我发现了一种更好的呈现剃刀视图页面的方法,该方法适用于Web表单环境和MVC环境。 No controller is needed. 无需控制器。

Here is the code example, in this example I simulated a mvc action with an async http handler: 这是代码示例,在此示例中,我使用异步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());
        }
    }

The easiest way for me was:对我来说最简单的方法是:

  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;
   }

I use this for emails and make sure that the file only contains CSS and HTML我将其用于电子邮件并确保该文件仅包含 CSS 和 HTML

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM