asp.net-mvc 如何将 ASP.NET MVC 视图呈现为字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/483091/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to render an ASP.NET MVC view as a string?
提问by Dan Atkinson
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
If I use this, I seem to be unable to do a redirectToAction, as it tries to render a view that may not exist. If I do return the view, it is completely messed up and doesn't look right at all.
如果我使用它,我似乎无法执行redirectToAction,因为它试图呈现可能不存在的视图。如果我确实返回视图,它会完全混乱并且看起来根本不正确。
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);
回答by Ben Lesh
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
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();
}
}
Edit: added Razor code.
编辑:添加了 Razor 代码。
回答by Dilip0165
This answer is not on my way . This is originally from https://stackoverflow.com/a/2759898/2318354but 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但在这里我展示了将它与“静态”关键字一起使用的方法,使其对所有控制器通用。
For that you have to make staticclass in class file . (Suppose your Class File Name is Utils.cs )
为此,您必须static在类文件中创建类。(假设您的类文件名是 Utils.cs )
This example is For Razor.
这个例子是 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”作为参数传递给控制器,通过在控制器文件中添加 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 的建议,此扩展方法也可以从控制器调用,如下所示
string result = this.RenderRazorViewToString("ViewName", model);
I hope this will be useful to you make code clean and neat.
我希望这对您使代码干净整洁有用。
回答by Tim Scott
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;
}
}
回答by LorenzCK
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 的响应流(它不允许您更改响应的 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 StringWriterused to write the view into the ViewContext:
更新:在 ASP.NET MVC 2.0 RC 中,代码稍有变化,因为我们必须传入StringWriter用于将视图写入的ViewContext:
//...
//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);
//...
回答by Jenny O'Reilly
This articledescribes how to render a View to a string in different scenarios:
本文介绍了不同场景下如何将一个View渲染成一个字符串:
- MVC Controller calling another of its own ActionMethods
- MVC Controller calling an ActionMethod of another MVC Controller
- WebAPI Controller calling an ActionMethod of an MVC Controller
- MVC 控制器调用另一个自己的 ActionMethods
- MVC 控制器调用另一个 MVC 控制器的 ActionMethod
- WebAPI 控制器调用 MVC 控制器的 ActionMethod
The solution/code is provided as a class called ViewRenderer. It is part of Rick Stahl's WestwindToolkit at GitHub.
解决方案/代码作为名为ViewRenderer的类提供。它是GitHub 上Rick Stahl 的WestwindToolkit 的一部分。
Usage(3. - WebAPI example):
用法(3. - WebAPI 示例):
string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));
回答by Josh Noe
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 引擎:https: //github.com/Antaris/RazorEngine
回答by Jayesh Patel
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)
回答by Marcin
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);
}
}
回答by Jeremy Bell
I am using MVC 1.0 RTM and none of the above solutions worked for me. But this one did:
我正在使用 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
回答by Adamy
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;
}
}

