我想输出两个不同的视图(一个作为将作为电子邮件发送的字符串),另一个显示给用户的页面.
这是否可以在ASP.NET MVC beta中使用?
我尝试过多个例子:
1. ASP.NET MVC Beta中的RenderPartial到String
如果我使用此示例,则会收到"在HTTP标头发送后无法重定向".
2. MVC框架:捕获视图的输出
如果我使用它,我似乎无法执行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);
Ben Lesh.. 563
这就是我想出来的,它对我有用.我将以下方法添加到我的控制器基类中.(您可以随时将这些静态方法设置为接受控制器作为参数的其他地方)
MVC2 .ascx风格
protected string RenderViewToString(string viewPath, T model) { ViewData.Model = model; using (var writer = new StringWriter()) { var view = new WebFormView(ControllerContext, viewPath); var vdd = new ViewDataDictionary (model); var viewCxt = new ViewContext(ControllerContext, view, vdd, new TempDataDictionary(), writer); viewCxt.View.Render(viewCxt, writer); return writer.ToString(); } }
剃刀.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代码.
这就是我想出来的,它对我有用.我将以下方法添加到我的控制器基类中.(您可以随时将这些静态方法设置为接受控制器作为参数的其他地方)
MVC2 .ascx风格
protected string RenderViewToString(string viewPath, T model) { ViewData.Model = model; using (var writer = new StringWriter()) { var view = new WebFormView(ControllerContext, viewPath); var vdd = new ViewDataDictionary (model); var viewCxt = new ViewContext(ControllerContext, view, vdd, new TempDataDictionary(), writer); viewCxt.View.Render(viewCxt, writer); return writer.ToString(); } }
剃刀.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代码.
这个答案不在我的路上.这最初来自/sf/ask/17360801/, 但在这里我已经展示了将它与"静态"关键字一起使用的方法,使其适用于所有控制器.
为此,你必须static
在类文件中创建类.(假设您的类文件名是Utils.cs)
这个例子是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(); } } }
现在,您可以通过将"this"作为参数传递给Controller,在控制器文件中添加NameSpace,从控制器调用此类.
string result = RazorViewToString.RenderRazorViewToString(this ,"ViewName", model);
根据@Sergey给出的建议,这个扩展方法也可以从cotroller调用,如下所示
string result = this.RenderRazorViewToString("ViewName", model);
我希望这对你使代码干净整洁有用.
这对我有用:
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; } }
我发现了一个新的解决方案,可以将视图呈现为字符串,而不必混淆当前HttpContext的响应流(它不允许您更改响应的ContentType或其他标头).
基本上,您所做的就是为视图创建一个假的HttpContext来呈现自己:
///Renders a view to string. 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(); } ///Fake IView implementation used to instantiate an HtmlHelper. 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上的Headers不会抛出" 服务器无法在发送HTTP标头后设置内容类型 "异常).
更新:在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); //...
本文介绍如何在不同的方案中将视图呈现为字符串:
MVC Controller调用另一个自己的ActionMethods
MVC Controller调用另一个MVC控制器的ActionMethod
WebAPI Controller调用MVC控制器的ActionMethod
解决方案/代码作为名为ViewRenderer的类提供.它是Rick Stahl 在GitHub的WestwindToolkit的一部分.
用法(3. - WebAPI示例):
string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));
如果你想完全放弃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
您将使用这种方式获取字符串视图
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)