我想输出两个不同的视图(一个作为将作为电子邮件发送的字符串),另一个显示给用户。
在ASP.NET MVC beta中可能吗?
我尝试了多个示例:
1. 在ASP.NET MVC Beta中将RenderPartial转换为字符串
如果使用此示例,则会收到“发送HTTP标头后无法重定向”。
如果使用此方法,则似乎无法执行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);
#1楼
我正在使用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
#2楼
我找到了一个新的解决方案,该视图可以将视图呈现为字符串,而不必弄乱当前HttpContext的Response流(不允许您更改响应的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中,代码有所变化,因为我们必须传入用于将视图写入ViewContext
的StringWriter
:
//...
//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);
//...
#3楼
如果您想完全放弃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 Engine: https : //github.com/Antaris/RazorEngine
#4楼
这个答案不在我身边。 这最初来自https://stackoverflow.com/a/2759898/2318354,但在这里,我展示了将其与“静态”关键字一起使用以使其对所有Controller通用的方法。
为此,您必须在类文件中创建static
类。 (假设您的类文件名是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的方式,通过在Controller File中添加NameSpace的方式从控制器中调用此类。
string result = RazorViewToString.RenderRazorViewToString(this ,"ViewName", model);
根据@Sergey的建议,此扩展方法也可以从cotroller调用,如下所示
string result = this.RenderRazorViewToString("ViewName", model);
我希望这对您使代码整洁有帮助。
#5楼
要重复一个更未知的问题,请看一下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"));
});
}
来源:oschina
链接:https://my.oschina.net/stackoom/blog/3160930