将视图以编程方式呈现为字符串

6
我希望能够在控制器中获取视图生成的HTML代码字符串,对其进行修改,然后将其添加到我的JsonResult中。我找到了一些可以从部分视图执行此操作的代码,但我想从aspx视图中执行此操作。
额外解释:假设我有一个名为Frame.aspx的页面,/Controller/Frame 将返回该页面。我想在输出响应之前获取它,以便我可以用jsonp包装它。我不想每次都在代码中编辑返回结果,这就是为什么我想以编程方式加载视图的原因。
当前,/Controller/Frame 返回Frame.aspx的内容:<html><body>hello</body></html> 假设有一个函数可以将视图呈现为字符串构建器。
StringBuilder sb = new StringBuilder();
RenderView(sb, "Frame");

现在将sb用jsonp包装起来:
public JsonResult Frame(string callback)
{
     StringBuilder sb = new StringBuilder();
     RenderView(sb, "Frame");

     return new JsonResult
     {
         Data = "(function() { " + callback + "(" +  clientResponse + "); })();"
         ,
         JsonRequestBehavior = JsonRequestBehavior.AllowGet
     };
}

1
可能是Render a view as a string的重复问题。 - Matt Mitchell
请编辑此问题并提供更多细节,可能还要附上一些示例代码。这里的细节不足以尝试回答。 - Brandon Satrom
请不要在标题中包含"C#"等标签,这只是多余的。将它们留在标签中就足够了。 - John Saunders
@Graphain 谢谢提供的链接。这差不多是我要找的,只是它不能用于mvc2。我进行了修改,但现在又遇到了另一个问题:当我返回一个JsonResult时,我的代码试图修改已经设置好的http头并抛出异常。我暂时会使用webclient来处理页面,直到找到更好的解决方案... - Abdo
RenderView必须将其发送到客户端以及字符串构建器。http://stackoverflow.com/questions/2746333/asp-net-mvc2-render-a-view-as-a-string看起来相关。 - Matt Mitchell
3个回答

19

这个方法非常好用(我是从stackoverflow上找到的)。

我是这样使用它的:

public class OfferController : Controller
{
    [HttpPost]
    public JsonResult EditForm(int Id)
    {
        var model = Mapper.Map<Offer, OfferEditModel>(_repo.GetOffer(Id));

        return Json(new { status = "ok", partial = this.RenderPartialViewToString("Edit", model) });
    }
}



public static partial class ControllerExtensions
{
    public static string RenderPartialViewToString(this ControllerBase controller, string partialPath, object model)
    {
        if (string.IsNullOrEmpty(partialPath))
            partialPath = controller.ControllerContext.RouteData.GetRequiredString("action");

        controller.ViewData.Model = model;

        using (StringWriter sw = new StringWriter())
        {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialPath);
            ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
            // copy model state items to the html helper 
            foreach (var item in viewContext.Controller.ViewData.ModelState)
                if (!viewContext.ViewData.ModelState.Keys.Contains(item.Key))
                {
                    viewContext.ViewData.ModelState.Add(item);
                }


            viewResult.View.Render(viewContext, sw);

            return sw.GetStringBuilder().ToString();
        }
    }
}

2
我该如何在.NET CORE中实现相同的功能? - Hasan A Yousef

1

这里有另一种解决MVC 2.0和.net 4.0中捕获视图的方法。我只是在Andrew原始内容中添加了几行代码。

 public static class ControllerExtensions
    {

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, null, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="masterPageName">The master page to use for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            string masterPageName,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, masterPageName, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this Controller controller,
            TController targetController,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(targetController, null, action);
        }




        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="masterPageName">The name of the master page for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        ///     
  public static string CaptureActionHtml<TController>(this Controller controller, TController targetController, string masterPageName, Func<TController, ViewResult>  action) where TController : Controller

       {
    if (controller == null)
    {
    throw new ArgumentNullException("controller");
    }
    if (targetController == null)
    {
    throw new ArgumentNullException("targetController");
    }
    if (action == null)
    {
    throw new ArgumentNullException("action");
    }
    // pass the current controller context to orderController
    var controllerContext = controller.ControllerContext;
    targetController.ControllerContext = controllerContext;

    // replace the current context with a new context that writes to a string writer
    var existingContext = HttpContext.Current;
    var writer = new StringWriter();
    var response = new HttpResponse(writer);
    var context = new HttpContext(existingContext.Request, response) { User = existingContext.User };
    HttpContext.Current = context;

    // execute the action
    var viewResult = action(targetController);

    // change the master page name
    if (masterPageName != null)
    {
    viewResult.MasterName = masterPageName;
    }

    // we have to set the controller route value to the name of the controller we want to execute
    // because the ViewLocator class uses this to find the correct view
    var oldController = controllerContext.RouteData.Values["controller"];
    controllerContext.RouteData.Values["controller"] = typeof(TController).Name.Replace("Controller", "");

    // execute the result
    viewResult.ExecuteResult(controllerContext);

    StringWriter sw = new StringWriter();
    var xx = targetController.TempData["pdf"];
    //var viewContext = new ViewContext(controllerContext, viewResult.View, new ViewDataDictionary(targetController.ViewData.Model), new TempDataDictionary(), sw);
    var viewContext = new ViewContext(controllerContext, viewResult.View, viewResult.ViewData, new TempDataDictionary(), sw);
    viewResult.View.Render(viewContext, HttpContext.Current.Response.Output);
    response.Flush();

    // restore the old route data
    controllerContext.RouteData.Values["controller"] = oldController;

    // restore the old context
    HttpContext.Current = existingContext;

    return sw.ToString();
    } 



    }
}

干杯!!


1
Mike Hadlow在博客中介绍了一个名为CaptureActionHtml()的函数,可以实现这个功能。我已经使用它将小型、易于管理的报告组合成大型报告,然后进行传递。

http://mikehadlow.blogspot.com/2008/06/mvc-framework-capturing-output-of-view_05.html

using System;
using System.IO;
using System.Web;
using System.Web.Mvc;

namespace Suteki.Common.Extensions
{
    public static class ControllerExtensions
    {
        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, null, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="masterPageName">The master page to use for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            string masterPageName,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, masterPageName, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this Controller controller,
            TController targetController, 
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(targetController, null, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="masterPageName">The name of the master page for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this Controller controller,
            TController targetController, 
            string masterPageName,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            if (controller == null)
            {
                throw new ArgumentNullException("controller");
            }
            if (targetController == null)
            {
                throw new ArgumentNullException("targetController");
            }
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            // pass the current controller context to orderController
            var controllerContext = controller.ControllerContext;
            targetController.ControllerContext = controllerContext;

            // replace the current context with a new context that writes to a string writer
            var existingContext = System.Web.HttpContext.Current;
            var writer = new StringWriter();
            var response = new HttpResponse(writer);
            var context = new HttpContext(existingContext.Request, response) {User = existingContext.User};
            System.Web.HttpContext.Current = context;

            // execute the action
            var viewResult = action(targetController);

            // change the master page name
            if (masterPageName != null)
            {
                viewResult.MasterName = masterPageName;
            }

            // we have to set the controller route value to the name of the controller we want to execute
            // because the ViewLocator class uses this to find the correct view
            var oldController = controllerContext.RouteData.Values["controller"];
            controllerContext.RouteData.Values["controller"] = typeof(TController).Name.Replace("Controller", "");

            // execute the result
            viewResult.ExecuteResult(controllerContext);

            // restore the old route data
            controllerContext.RouteData.Values["controller"] = oldController;

            // restore the old context
            System.Web.HttpContext.Current = existingContext;

            return writer.ToString();
        }
    }
}

这对我来说在V3.5和MVc1.0上运行良好。但是当我将其升级到V4.0和MVC 2.0时,上下文返回null..有什么想法吗? - Gokul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接