Rendering an ASP.NET MVC View to a string in a Web Api Controller

Today I was working on a Web Api Controller that integrated with a third party application. The application wants us to return an XML document. One of the nodes is a piece of HTML that is displayed in the application.

So I started thinking about a flexible way to render HTML from a WebApi Controller. After searching around on the web I came up with a solution.

Sneaking my way into MVC

The biggest problem was that rendering a View is something that normally shouldn’t happen inside Web Api (see this discussion why not). The solutions I found on the web all use some kind of MediaTypeFormatter to return HTML when the client requests it.
However, that’s not what I want. I want to return the following data to the client where the Body property should contain an HTML string:
   1:  public class ContentItem
   2:  {
   3:      public string Title { get; set; }
   4:      public string Body { get; set; }
   5:      public string Widgets { get; set; }
   6:  }

Eventually this is what I came up with:

   1:  public static string RenderViewToString(string controllerName, 
   2:                                          string viewName, 
   3:                                          object viewData)
   4:  {
   5:      var context = HttpContext.Current;
   6:      var contextBase = new HttpContextWrapper(context); 
   7:      var routeData = new RouteData();     
   8:      routeData.Values.Add("controller", controllerName);     
   9:   
  10:      var controllerContext = new ControllerContext(contextBase,  
  11:                                                    routeData,    
  12:                                                    new EmptyController());
  13:   
  14:      var razorViewEngine = new RazorViewEngine();
  15:      var razorViewResult = razorViewEngine.FindView(controllerContext,    
  16:                                                     viewName,     
  17:                                                     "",   
  18:                                                     false);
  19:   
  20:      var writer = new StringWriter();
  21:      var viewContext = new ViewContext(controllerContext, 
  22:                                        razorViewResult.View,              
  23:                                        new ViewDataDictionary(viewData),   
  24:                                        new TempDataDictionary(), 
  25:                                        writer);
  26:      razorViewResult.View.Render(viewContext, writer);
  27:   
  28:      return writer.ToString();
  29:  }
  30:   
  31:  class EmptyController : ControllerBase
  32:  {
  33:      protected override void ExecuteCore() { }
  34:  }

You can simply call this from anywhere in your Web Api Controller:

RenderViewToString(“controller”, “view”, model)

Have you run into similar issues? Can you use this code or do you have another idea? Please leave a comment!

Wouter de Kort works as a lead architect and consultant. He helps organizations stay on the cutting edge of software development. Wouter focuses on DevOps. He loves solving complex problems and helping other developers to grow. Wouter authored the book DevOps on the Microsoft stack and a couple of other books. Wouter is a Microsoft MVP and an ALM Ranger. You can find him on Twitter (@wouterdekort), on his blog at wouterdekort.com and at the various conferences where Wouter speaks.

Share

8 Responses

  1. Application hosted in IIS It shows the error “The virtual path ‘/’ maps to another application, which is not allowed.”

    • Can you share some more details on your IIS configuration? Are you nesting your applications?

      • I’m getting the same issue: “The virtual path ‘/’ maps to another application, which is not allowed.” My web api is deployed as an application inside a web site. Specifically I have in IIS, Default Web Site and inside I have an application MyApi, everything works fine but only the code above is failing.

  2. My ApiController Code is
    public class ValuesController : ApiController
    {
    // GET api/values
    public HttpResponseMessage Get()
    {
    object model = new { Name = “World” };
    string html = RenderViewToString(“Home”, “~/views/Home/Test.cshtml”, model);
    return Request.CreateResponse(HttpStatusCode.OK, new { content = html });
    }

    // GET api/values/5
    public string Get(int id)
    {
    return “value”;
    }

    // POST api/values
    public void Post([FromBody]string value)
    {
    }

    // PUT api/values/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/values/5
    public void Delete(int id)
    {
    }

    public static string RenderViewToString(string controllerName,
    string viewName,
    object viewData)
    {
    var context = HttpContext.Current;
    var contextBase = new HttpContextWrapper(context);
    var routeData = new RouteData();
    routeData.Values.Add(“controller”, controllerName);

    var controllerContext = new System.Web.Mvc.ControllerContext(contextBase,
    routeData,
    new EmptyController());

    var razorViewEngine = new System.Web.Mvc.RazorViewEngine();
    var razorViewResult = razorViewEngine.FindView(controllerContext,
    viewName,
    “”,
    false);

    var writer = new StringWriter();
    var viewContext = new System.Web.Mvc.ViewContext(controllerContext,
    razorViewResult.View,
    new System.Web.Mvc.ViewDataDictionary(viewData),
    new System.Web.Mvc.TempDataDictionary(),
    writer);
    razorViewResult.View.Render(viewContext, writer);

    return writer.ToString();
    }
    }
    class EmptyController: System.Web.Mvc.ControllerBase
    {
    protected override void ExecuteCore() { }
    }

    this statement razorViewResult.View.Render(viewContext, writer); execution. i am get below error
    System.Web.HttpCompileException: ‘c:\users\praveen\source\repos\RazorEngineAPI\RazorEngineAPI\views\Home\Test.cshtml(5): error CS1503: Argument 1: cannot convert from ‘method group’ to ‘HelperResult”

  3. In addition, you can create your own pluggable implementation of Razor to host in your own applications. The two provided implementations are marginally different in behavior; they are optimized for their respective environments. With Razor, you can create a custom Razor Engine that can be used in any .NET environment. In this article, I focus on the ASP.NET MVC Razor implementation specifically.

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.