MVC Pro Tip: Rendering A ViewResult As HTML String Output

1 April 2015

Perhaps you've tried this - creating a controller, getting the ViewResult and attempting to convert it to a HTML string (for example, my use case was for an email templating / generation system).

It's not terribly difficult, but there are a few caveats - primarily, that when you just "new" up a Controller outside of the proper MVC lifecycle, it doesn't have a proper ControllerContext associated to it, which is something you'll need to handle.

On to the code for those uninterested in the preamble and just want to get stuff done. There's two main parts - a function to create a controller with a proper Context, and another to render a view as string (given a view name, controller context and model data). Here's how to use it:

MyController controller = ControllerHelper.CreateController<MyController>();

System.Web.Mvc.ViewResult result = controller.MyAction(actionParam) as System.Web.Mvc.ViewResult;

string htmlOutput = ControllerHelper.RenderViewToString(controller.ControllerContext, "MyAction", result.ViewData.Model); 

And the ControllerHelper class:

public static class ControllerHelper
{        
    public static T CreateController<T>(RouteData routeData = null)
        where T : Controller, new()
    {
        // create a disconnected controller instance
        T controller = new T();

        // get context wrapper from HttpContext if available
        HttpContextBase wrapper = null;
        if (System.Web.HttpContext.Current != null)
            wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
        else
            throw new InvalidOperationException(
                "Can't create Controller Context if no active HttpContext instance is available.");

        if (routeData == null)
            routeData = new RouteData();

        // add the controller routing if not existing
        if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
            routeData.Values.Add("controller", controller.GetType().Name
                                                        .ToLower()
                                                        .Replace("controller", ""));

        controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
        return controller;
    }



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

        ViewDataDictionary viewData = new ViewDataDictionary(model);

        using (var 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 sw.GetStringBuilder().ToString();
        }
    }   
}

Don't forget that you need to use the CreateController class, or RenderViewToString won't work (as ControllerContext will be null). Thanks to the following blogs that provided the solution - I'm simply passing it on :)

http://weblog.west-wind.com/posts/2013/Jul/15/Rendering-ASPNET-MVC-Razor-Views-outside-of-MVC-revisited http://sharpercoder.blogspot.co.uk/2013/11/mvc-partial-view-to-string.html

Tags: MVC, HTML, Controller, View

Add a Comment

No Comments