Render partial view to string in ASP.NET MVC

ASP.NET MVC logo

A common need I have in my ASP.NET MVC based projects (and other people have too, as it appears) is to render a complete "View" to string instead of the http response and then present it or embed it in another rendered view.

Finding a solution isn't very easy because view rendering (using the default WebForms view engine) is tightly coupled with the response stream and therefore you have to intercept the output in some rather complex way. There are several different solutions online, for instance using filters on the HttpResponse or taking the view as a WebForms control and rendering it directly.

Both solutions work, but have some drawbacks. The severity of these limitations depend on what you are trying to do and might not be an issue for your specific case. 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.

The second solution, which instantiates the target view as a Control class and then uses the RenderControl() method to write its content to a string, doesn't pass through the standard MVC view rendering engine. The view is rendered directly and must derive from the Control class, if you need any View Data, you must provide it from the code that wants to render the view.

I found another solution which I posted on StackOverflow some time ago. It is quite heavy weight if compared to the other two solutions (I have no benchmarks actually I have some now), but as far as I know it has none of the mentioned drawbacks.

What I do is create a simple HttpResponse wrapping a StringBuilder to get the view's output, instantiate a "fake" HttpContext with some routing data and the HttpResponse and then use the default RenderPartial() method provided by ASP.NET MVC. This is an extensions method of the HtmlHelper that processes a partial view through the default MVC view engine and then renders it to the response stream. But in this case the server response is hijacked to the fake HttpContext and the output can then be easily accessed through the StringBuilder instance.

Here's the code:

/// <summary>Renders a view to string.</summary>
public static string RenderPartialToString(this HtmlHelper html, string viewName, object viewData) {
	return RenderViewToString(html.ViewContext.Controller.ControllerContext, viewName, viewData);
}
/// <summary>Renders a view to string.</summary>
public static string RenderViewToString(this Controller controller, string viewName, object viewData) {
	return RenderViewToString(controller.ControllerContext, viewName, viewData);
}

private static string RenderViewToString(ControllerContext context,
                                        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),
        context.RouteData, context.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, only 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
}

Doesn't look particularly pretty and probably isn't too fast, but this code allows you to leverage most of the features provided by ASP.NET MVC and doesn't force you to flush the HttpResponse (you'll still be able to add headers after rendering the view to string).

A (pretty useless) example of partial request from a WebForms view:

<h2>Comments</h2>
<% foreach(var comment in Model.Comments) { %>
	var sComment = this.RenderViewToString("Comment", comment);
	Response.Write(ApplyEmoticons(sComment));
<% } %>

Or from a controller:


public class CommentController : Controller {
	public ActionResult PublishComment(){
		Comment comment;
		// ... process comment ...
		
		return Json(new {
			result = "success",
			timestamp = DateTime.UtcNow,
			comment = this.RenderToString("Comment", comment)
		});
	}
}

The code above shows how to render a view and then embed the resulting HTML in a Json response (to an AJAX call for instance).

Update: check out the benchmarks of the three methods.