Capturing HTML to String in MVC2.0

跟風遠走 提交于 2019-12-02 06:48:46

问题


I am new to MVC2.0. I use the below code to capture HTML and return it as a string. But this works fine in MVC1.0 and .NET 3.5 framework. Recently I upgraded the code to .NET 4.0 and MVC 2.0. Now this is no longer working and the context returns null. when I explore I saw two errors 1."This operation requires IIS integrated pipeline mode." 2.OutputStream = 'response.OutputStream' threw an exception of type system.Web.HttpException'

Here is the calling function

var htmlstring = this.CaptureActionHtml(new PdfController(_paService, _configService), c => (ViewResult)paController.GetHTML(p));

//and the capture method.

  /// <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();
        }

I found a work around.. but struck in another problem.. The model was not passing through the view..Here is the updated code

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 viewContext = new ViewContext(controllerContext, viewResult.View, new ViewDataDictionary(controllerContext.Controller.ViewData.Model), new TempDataDictionary(), sw);
    var viewContext = new ViewContext(controllerContext, viewResult.View, targetController.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();
    } 

any suggestions will be really helpful..


回答1:


Thanks for reviewing.. I found it myself.. silly me!! its just simple thing.. Here is the full code..

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();
    } 

I just replaced this line of code

var viewContext = new ViewContext(controllerContext, viewResult.View, new ViewDataDictionary(targetController.ViewData.Model), new TempDataDictionary(), sw);

with

var viewContext = new ViewContext(controllerContext, viewResult.View, viewResult.ViewData, new TempDataDictionary(), sw);

It works fine.. Cheers!!



来源:https://stackoverflow.com/questions/5553674/capturing-html-to-string-in-mvc2-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!