Asp – PartialView as string + JsonResult

asp.net-mvc

I'm just looking for a nice way how to return JSON object from server that contains html of partial view and some additional data if needed.

I'm using approach by Tim Scott to render partialview as string (with some modifications – made it to be viewenginescollection aware, made it to understand difference between view and partial view, put it into StringResult class and wrapped around with controller extension method).

Here's what causes trouble:

public static string RenderViewToString(ControllerContext controllerContext,
            IView view, ViewDataDictionary viewData, TempDataDictionary tempData)
        {
            Stream filter = null;
            ViewPage viewPage =
               new ViewPage
               {
                   ViewContext = new ViewContext
                       (controllerContext, view, viewData, tempData)
               };
            //Right, create our view

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

usage looks like this:

var v = this.ViewResultToString(PartialView("_Foo", foo));
return Json(new {Html = Server.HtmlEncode(v), Bar = foo.Bar});

However – this throws an exception:

Server cannot set content type after HTTP headers have been sent.

Here's a stack trace:

[HttpException (0x80004005): Server cannot set content type after HTTP headers have been sent.]
System.Web.HttpResponse.set_ContentType(String value) +8760264
System.Web.HttpResponseWrapper.set_ContentType(String value) +11
System.Web.Mvc.JsonResult.ExecuteResult(ControllerContext context) +131
System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +10
System.Web.Mvc.<>c__DisplayClass11.b__e() +20
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func1 continuation) +255
System.Web.Mvc.<>c__DisplayClass13.<InvokeActionResultWithFilters>b__10() +20
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList
1 filters, ActionResult actionResult) +179
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +395
System.Web.Mvc.Controller.ExecuteCore() +123
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +23
Company.UI.Project.Controllers.Base.BaseController.Execute(RequestContext requestContext) in c:\Project\Controllers\Base\BaseController.cs:109
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +144
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +54
System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

Thing is – whenever i call RenderViewToString, an exception will be thrown if I'm going to return any other ActionResult that's not ContentResult.

So – is there another nice way how to serialize anonymous object into Json without 3rd party tools
OR
how to make this approach to work (how to kill that damn exception,
what exactly forces those headers to be sent)?

Best Answer

Got it working using this.

This was the problem:

In the first case, intercepting the output to HttpResponse using a "capturing filter" forces you to flush the output before the whole view is rendered and, since the original HttpResponse object is used, doesn't allow you to change content encoding, mime type or add headers after the partial view has been rendered.

Solution - not to use 'flushing' technique to render partial view.

Related Topic