将ActionResult渲染为字符串 – 以错误的顺序出现

我尝试将ActionResult渲染为字符串。 我这样做是通过传入我自己的HttpContext ,用我自己的TextWriter替换输出文本TextWriter

这是问题 – 元素渲染不正常。 如果我通过浏览器直接查询部分视图,它可以正常工作。 如果我通过我的替换文本编写器渲染它,则首先渲染剃刀视图中的任何@ Html.Action元素,而不管它们在视图中的位置如何。

所以,这是我的Razor视图:

 @inherits System.Web.Mvc.WebViewPage @using System.Web.Mvc.Html; 
@Model.DebugText
@foreach (var item in @Model.Items) {
@item.Title
@Html.Action( "LayoutItem", new { id = item.Id, uniqueName = item.UniqueName } ); }

如果我直接通过浏览器查询视图,它将以正确的顺序呈现:

  • @ Model.DebugText
  • Item1.Title
  • Item1动作渲染
  • Item2.Title
  • Item2动作渲染

如果我将其呈现给我的TextWriter,它将按以下顺序呈现:

  • Item1动作渲染
  • Item2动作渲染
  • @ Model.DebugText
  • Item1.Title
  • Item2.Title

为什么?

这就是我如何替代Text编写器。 (我从ASP.NET WebForms页面调用它,因此已经有一个现有的HttpContext

 public static class ActionResultExtensions { internal class MyResponseWrapper : HttpResponseWrapper { private System.IO.TextWriter _textWriter; public MyResponseWrapper(HttpResponse wrappedResponse, System.IO.TextWriter textWriter) : base(wrappedResponse) { _textWriter = textWriter; } public override System.IO.TextWriter Output { get { return this._textWriter; } set { this._textWriter = value; } } } internal class MyHttpContextWrapper : HttpContextWrapper { private readonly System.IO.TextWriter _textWriter; public MyHttpContextWrapper(System.IO.TextWriter textWriter) : base(HttpContext.Current) { this._textWriter = textWriter; } public override HttpResponseBase Response { get { var httpResponse = HttpContext.Current.Response; return new MyResponseWrapper(httpResponse, this._textWriter); } } } public static void Render(this System.Web.Mvc.ActionResult result, System.IO.TextWriter textWriter, System.Web.Routing.RouteData routeData, System.Web.Mvc.ControllerBase controllerBase) { var httpContextWrapper = new MyHttpContextWrapper(textWriter); result.ExecuteResult(new System.Web.Mvc.ControllerContext(httpContextWrapper, routeData, controllerBase)); } } public static class MvcUtils { public static void RenderControllerAction(Func f, System.IO.TextWriter writer) where T : System.Web.Mvc.ControllerBase, new() { var controller = new T(); // We have to initialise the RouteData so that it knows the name of the controller // This is used to locate the view var typeName = controller.GetType().Name; System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex("(.*)Controller$"); var match = regex.Match(typeName); if (match.Success) { typeName = match.Groups[1].Value; } var routeData = new System.Web.Routing.RouteData(); routeData.Values.Add("controller", typeName); var actionResult = f(controller); actionResult.Render(writer, routeData, controller); } } 

然后我最终使用以下代码将其输出到字符串:

 StringBuilder sb = new StringBuilder(); StringWriter stringWriter = new StringWriter(sb); CMS.Website.MvcUtils.RenderControllerAction ( c => c.ScreenLayout(this.MgrPlayerGroupViewer.ScreenLayoutId), stringWriter ); stringWriter.Flush(); var generatedString = sb.ToString(); 

我已经为TextWriter编写了一个拦截器,果然,它正在接收三次Write(string)调用

  • 写(LayoutItem 1动作内容)
  • 写(LayoutItem 2动作内容)
  • 写(Model.DebugText和两个项目标题)

大约6个月前我经历了这个。 目标是使用partial来填充jquery弹出对话框。

问题是View Engine想要以它自己的尴尬顺序渲染它们……

试试这个。 LMK如果需要澄清的话。

  public static string RenderPartialViewToString(Controller thisController, string viewName, object model) { // assign the model of the controller from which this method was called to the instance of the passed controller (a new instance, by the way) thisController.ViewData.Model = model; // initialize a string builder using (StringWriter sw = new StringWriter()) { // find and load the view or partial view, pass it through the controller factory ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(thisController.ControllerContext, viewName); ViewContext viewContext = new ViewContext(thisController.ControllerContext, viewResult.View, thisController.ViewData, thisController.TempData, sw); // render it viewResult.View.Render(viewContext, sw); //return the razorized view/partial-view as a string return sw.ToString(); } }