ASP.NET MVC Render Partial View to String


I have run into a situation where I would like to render a partial view to a string and then return it as part of a JSON response like so:

return Json(new {
    statusCode = 1,
    statusMessage = "The person has been added!",
    personHtml = PartialView("Person", person)
});

The ability to do something like this would open up a ton of amazing possibilities, so I really scoured the internet looking for a solution. Unfortunately, no one seems to have come up with a clean solution for it, so I dug into the MVC code and came up one…and because I’m such a nice guy, you get to copy it for free. ;)

public abstract class MyBaseController : Controller {

    protected string RenderPartialViewToString()
    {
        return RenderPartialViewToString(null, null);
    }

    protected string RenderPartialViewToString(string viewName)
    {
        return RenderPartialViewToString(viewName, null);
    }

    protected string RenderPartialViewToString(object model)
    {
        return RenderPartialViewToString(null, model);
    }

    protected string RenderPartialViewToString(string viewName, object model)
    {
        if (string.IsNullOrEmpty(viewName))
            viewName = ControllerContext.RouteData.GetRequiredString("action");

        ViewData.Model = model;

        using (StringWriter sw = new StringWriter()) {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);

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

Now you can simply do this:

public class MyController : MyBaseController {

    public ActionResult CreatePerson(Person p) {
        if (ModelState.IsValid) {
            try {
                PersonRepository.Create(p);
                return Json(new {
                    statusCode = 1,
                    statusMessage = "The person has been added!",
                    personHtml = RenderPartialViewToString("Person", p)
                });
            }
            catch (Exception ex) {
                return Json(new {
                    statusCode = 0,
                    statusMessage = "Error: " + ex.Message
                });
            }
        }
        else
            return Json(new {
                statusCode = 0,
                statusMessage = "Invalid data!"
            });
    }
}

Also note that you can modify these functions to render a View (rather than a PartialView) with this small change:

ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName);

Enjoy!

91 thoughts on “ASP.NET MVC Render Partial View to String

  1. Just as an update, you could wrap the StringWriter inside a using statement:

    protected string RenderPartialViewToString(string viewName, object model)
    {
    if (string.IsNullOrEmpty(viewName))
    viewName = ControllerContext.RouteData.GetRequiredString(“action”);

    ViewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
    ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
    ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
    viewResult.View.Render(viewContext, sw);

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

  2. I would love to use this solution, but I am running into an error!

    viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);

    Error 80 ‘System.Web.Mvc.ViewContext’ does not contain a constructor that takes ‘5’ arguments

    Am I forgetting something?

  3. Brilliant,

    I’ve tried a whole bunch of different solutions and this is the best and simplest. Supports embedded Html helpers too.

    • If you’re asking how to render a View that is located on another controller path, I’m pretty sure you can just do this:

      RenderPartialViewToString(“~/Path/To/View.aspx”)

      I hope that answers your question.

  4. I’ve the same problem that Joris has. I use ASP.NET MVC 1 and all the team use it, so I can’t upgrade it to ASP.NET MVC 2.0, any ideas ?

    • Kip, I would suggest digging through the MVC code. It is open source and that is exactly what I did to come up with this solution. MVC 1 probably isn’t that much different. Find the code that renders a view and then just copy/modify the code around it into a utility function that you can use to render partials.

  5. A solution with HtmlHelper extension method, but not tested in all situation for the moment …

    public static string RenderPartialToString(this HtmlHelper html, string viewName, object viewData)
    {
    var baseOutput = html.ViewContext.Writer;

    StringWriter sw = new StringWriter();

    try
    {
    html.ViewContext.Writer = sw;
    html.RenderPartial(viewName, viewData);
    }
    catch(Exception ex )
    {
    html.ViewContext.Writer = baseOutput;
    sw.Dispose();
    return ex.Message + ex.StackTrace;
    }

    html.ViewContext.Writer = baseOutput;
    sw.Flush();
    return sw.ToString();
    }

  6. Wouldn’t an extension to ViewResultBase be neater? e.g. in the Controller:

    var viewText = View().RenderToString(this);

    var partialViewText = PartialView().RenderToString(this);

    // or with a named view and model

    var namedViewText = View(“Index”, model).RenderToString(this);

    the extension method would then just be

    public static string RenderToString(this ViewResultBase viewResult, Controller controller)
    {
    // init string writer
    using (StringWriter sw = new StringWriter())
    {
    // get the view context
    var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);

    // render the view
    viewResult.View.Render(viewContext, sw);

    // return the string
    return sw.ToString();
    }
    }

    and you’d only require one

  7. I’ve just realised this won’t quite work as the viewResult.View property will be null without a call to ViewEngineCollection.FindView or FindPartialView so we would need two methods, one for ViewResult and one for PartialViewResutl and before the line where we create the ViewContext we’d need to call either:

    viewResult.View = viewResult.ViewEngineCollection.FindView(controller.ControllerContext, viewResult.ViewName, viewResult.MasterName).View;

    or

    viewResult.View = viewResult.ViewEngineCollection.FindPartialView(controller.ControllerContext, viewResult.ViewName).View;

    Serves me right for posting without checking the code

  8. I have made this extension to HtmlHelper classe because I don’t call Render() and RenderPartial() in Controller / View, I call render in my own classes which have a reference to the ViewPage HtmlHelper.

    I have an HtmlHelper extension which initialize my own control factory, and this control factory give the reference of the Htmlhelper to all controls created by this factory.

  9. Awsome cant wait to try….wondering if i have no update target, i can’t really use the client side validation that mvc2 has..which sucks

  10. I am new to MVC. Can anyone please post an example of how to use the json html result in view. Thanks.

    • Consuming the JSON result actually has very little to do with MVC. It will be consumed via JavaScript. You will make an AJAX request to the server and then process the result accordingly. For example, if you have an MVC action that returns the same result I used in my blog post, then you can consume the result using jQuery as follows:

      $.post(‘/addUser’, userData, function(result) {
      alert(result.statusMessage);
      if (result.statusCode == 1) {
      var newPerson = $(result.personHtml);
      $(“#people”).append(newPerson);
      }
      });

  11. I wish we were using MVC 2.0. As

    viewResult.View.Render(viewContext, sw);

    Doesn’t write anything to my streamwriter. I’ve seen another solution where it mucks around with fake contexts, but I need session variables in my view so it errors if I try doing that. :(

  12. I need to render partial views to build a CMS. I want to dynamically load views (from a place where user installs modules, which are in essence, ASP.NET MVC Views) and then render them in the server (execute them, and get the result out of them), and then put it inside the specified place. Your code was a good starting point for me. However, still I’m not able to figure out how to do that. Thanks.

  13. Wonderful. Here my edited version so code can be moved outside the controller (maybe in a helper file)

    public static string RenderViewToString(ControllerContext context, ViewDataDictionary viewData, TempDataDictionary tempData, string viewName, object model)
    {
    if (string.IsNullOrEmpty(viewName))
    viewName = context.RouteData.GetRequiredString(“action”);

    viewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
    ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(context, viewName);
    ViewContext viewContext = new ViewContext(context, viewResult.View, viewData, tempData, sw);
    viewResult.View.Render(viewContext, sw);

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

    Then can be called from the controller like this:

    MyHelper.RenderViewToString(this.ControllerContext, this.ViewData, this.TempData, “_PartialViewName”, MyObject)

    In my case I made two versions, one requiring only the controller, viewname and model, since in most of the cases we only need to render simple partial views that work with model or viewmodel.

    • Probably, but its more work, especially if you need to do it often. Its also not really a traditional use of the view considering ASP.NET MVC has JSON functionality built right into the controller. Good suggestion, though!

    • Nope, didn’t get it from there. It took me several hours of digging through the MVC code to come up with my solution. I’m guessing you came up with your solution in the same manner, which is why they are so similar.

    • This really helped me, thanks. Here’s how I removed the “\r”…etc:

      Change to return MvcHtmlString:
      public static MvcHtmlString RenderViewToString(ControllerContext context, string viewName, object model)

      return MvcHtmlString.Create(sw.GetStringBuilder().ToString());

      Here’s the entire:

      public static MvcHtmlString RenderViewToString(ControllerContext context, string viewName, object model)
      {
      if (string.IsNullOrEmpty(viewName))
      viewName = context.RouteData.GetRequiredString(“action”);

      ViewDataDictionary viewData = new ViewDataDictionary(model);

      using (StringWriter sw = new StringWriter())
      {
      ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(context, viewName);
      ViewContext viewContext = new ViewContext(context, viewResult.View, viewData, new TempDataDictionary(), sw);
      viewResult.View.Render(viewContext, sw);

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

      • I do not see how MvcHtmlString-ifying should remove any tabs or line breaks. In fact, it does not. MvcHtmlString does not modify the wrapped string at all. To get rid of the above mentioned items you could use something like TidyNet or TidyManaged. Though, this is pretty much a side-effect. BTW, why would you want to do this?

  14. Hey, this is super helpful. I’m having some trouble unit testing it, though; I need to somehow mock the ViewEngines.Engines.FindPartialView(ControllerContext, viewName) call. Mocking up up a fake ControllerContext is straightforward enough, but I can’t find a way to set something up for the FindPartialView method.

    I’ve tried http://stackoverflow.com/questions/3621961/asp-net-mvc-unit-testing-renderpartialviewtostring-with-moq-framework, but without luck (what is the RouteConfigurator class?) and http://www.atlanticbt.com/blog/asp-net-mvc-using-ajax-json-and-partialviews/ (similar idea, same problem). Any thoughts? How did you unit test your example?

  15. It works fine if I don’t have a nested partial. If I have @Html.Partial(“_PartialName”, Model) instead of a partial that is rendered to string, it leaves out the nested partial.

    Any suggestions?

  16. How can I get a nested partial view to render with this logic?

    Ex. From a partial with name “_ParentPartial”

    @Html.Partial(“_ChildPartialName”, Model)

  17. Hi Just wondering why you wouldn’t just do

    return sw.ToString();

    rather than

    return sw.GetStringBuilder().ToString();

  18. I also am having trouble unit testing this – please post how you unit tested it (or verify that you didn’t)

    • This is what I actually came here for too. As far as I can see this shortcoming is due to classical Asp.Net on top of which Mvc resides. Despite a lot of horse-trading* there has been no solution to the problem yet.

      *precompiled view, custom view engine, in-memory-view-rendering, headless browser, hosting Asp.Net app from disk location, layers and layers of indirection, etc.

  19. Good post thanks!. However when the partial view is rendering it is adding extra blank spaces.
    eg:

    please look at the value attribute of the input element.

    Have any idea?

  20. Hey Crafty.

    Thanks for your article which had helpled me in rendering the partial view as string from controller.
    But is there any way to UnitTest this method as i am having nightmare in testing this method.
    If you already have any sample that would really help me a lot..

    Thank you

  21. Hi Crafty, I tried to implement your code in MVC 4, but I generated an error to this:
    ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName, “”); This error says: Value cannot be null.
    Parameter name: controllerContext. Please help, Thanks a lot.

  22. Superb work.

    I’m getting an error when executing this line:

    viewResult.View.Render(viewContext, sw);

    “Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property”

    This problem only occured when trying to convert large data such as 10k rows and above.

    Thank you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s