It seems, most code for rendering view into string doesn't work in MVC 5.
I have latest MVC 5.1.2 templates and I am trying to render view into string.
public static String RenderViewToString(ControllerContext context, String viewPath, object model = null)
{
context.Controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindView(context, viewPath, null);
var viewContext = new ViewContext(context, viewResult.View, context.Controller.ViewData, context.Controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(context, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
Well, it's working but its output contains lots of $ marks instead tags. I read something about it was fixed in RC version, but that's old news.
Problem looks like this
<$A$><h1></h1>
<table</$A$><$B$> class=""</$B$><$C$>> <tbody</$C$><$D$></$D$><$E$>></tbody>
</table></$E$>
I would like to ask, how do you render views into string in latest MVC 5 template ? Thanks.
Typically, Razor rendering in ASP.NET MVC is reserved purely for view rendering and generation of HTML output as part of an MVC request. But, as I describe in this article, it's also possible to render MVC Razor views directly to string in order to capture the output and to render those views outside of the direct context of an MVC Web request.
You must pass in the original StringWriter you are using to write to the StringBuilder, not a new instance or the output of the view will be lost. This article describes how to render a View to a string in different scenarios: The solution/code is provided as a class called ViewRenderer. It is part of Rick Stahl's WestwindToolkit at GitHub.
In ASP.NET MVC release 1.0 there are a couple of RenderPartial extension methods. The one I'm using in particular is System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial (this HtmlHelper, string, object). I'm unaware whether the method has been added in the latest revisions of MVC and wasn't present in earlier ones. Thanks.
The ASP.NET MVC Razor implementation is closely tied to ASP.NET and MVC and the View template (WebViewPage class) includes a few MVC-specific features as well as back references to the controller. In other words MVC Razor is designed with MVC in mind. Not surprisingly, it's easiest to render MVC Razor views out of MVC applications.
Ok, seems I found a solution. Author of the idea is Yakir Manor.
class FakeController : ControllerBase
{
protected override void ExecuteCore() { }
public static string RenderViewToString(string controllerName, string viewName, object viewData)
{
using (var writer = new StringWriter())
{
var routeData = new RouteData();
routeData.Values.Add("controller", controllerName);
var fakeControllerContext = new ControllerContext(new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://google.com", null), new HttpResponse(null))), routeData, new FakeController());
var razorViewEngine = new RazorViewEngine();
var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);
var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View, new ViewDataDictionary(viewData), new TempDataDictionary(), writer);
razorViewResult.View.Render(viewContext, writer);
return writer.ToString();
}
}
}
It's a trick with fake context and response.
Example:
String renderedHTML = RenderViewToString("Email", "MyHTMLView", myModel );
My file MyHTMLView.cshtml is stored in Views/Email/MyHTMLView.cshtml. Email is a fake controller name.
following is the solution that works with session and areas on MVC5.
public class FakeController : ControllerBase
{
protected override void ExecuteCore() { }
public static string RenderViewToString(string controllerName, string viewName,string areaName, object viewData,RequestContext rctx)
{
try
{
using (var writer = new StringWriter())
{
var routeData = new RouteData();
routeData.Values.Add("controller", controllerName);
routeData.Values.Add("Area", areaName);
routeData.DataTokens["area"] = areaName;
var fakeControllerContext = new ControllerContext(rctx, new FakeController());
//new ControllerContext(new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://google.com", null), new HttpResponse(null))), routeData, new FakeController());
fakeControllerContext.RouteData = routeData;
var razorViewEngine = new RazorViewEngine();
var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);
var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View, new ViewDataDictionary(viewData), new TempDataDictionary(), writer);
razorViewResult.View.Render(viewContext, writer);
return writer.GetStringBuilder().ToString();
//use example
//String renderedHTML = RenderViewToString("Email", "MyHTMLView", myModel );
//where file MyHTMLView.cstml is stored in Views/Email/MyHTMLView.cshtml. Email is a fake controller name.
}
}
catch (Exception ex)
{
//do your exception handling here
}
}
}
here is how you call this from another controller
var modal = getModal(params);
return FakeController.RenderViewToString(controllerName, viewName, areaName, modal, this.Request.RequestContext);
using requestcontext we can easily pass current session in fakecontroller and render razor string.
I had an immediate need to return 6 partial views as strings in a JSON object. Instead of creating a static method, and passing all the unneeded parameters, I decided to add protected methods to our ControllerBase class that derives from Controller, and is used as the base class for all of our controllers.
Here is a fully functional ControllerBase class that provides this functionality, and works very similar to the PartialView() and View() methods that are in the Controller class. It includes the additions from @Alok.
public abstract class ControllerBase : Controller
{
#region PartialViewToString
protected string PartialViewToString(string partialViewName, object model = null)
{
ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindPartialView(controllerContext, partialViewName) ?? throw new FileNotFoundException("Partial view cannot be found."),
model
);
}
#endregion
#region ViewToString
protected string ViewToString(string viewName, object model = null)
{
ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
model
);
}
protected string ViewToString(string viewName, string controllerName, string areaName, object model = null)
{
RouteData routeData = new RouteData();
routeData.Values.Add("controller", controllerName);
if (areaName != null)
{
routeData.Values.Add("Area", areaName);
routeData.DataTokens["area"] = areaName;
}
ControllerContext controllerContext = new ControllerContext(HttpContext, routeData, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
model
);
}
#endregion
#region Private Methods
private string ViewToString(ControllerContext controllerContext, ViewEngineResult viewEngineResult, object model)
{
using (StringWriter writer = new StringWriter())
{
ViewContext viewContext = new ViewContext(
controllerContext,
viewEngineResult.View,
new ViewDataDictionary(model),
new TempDataDictionary(),
writer
);
viewEngineResult.View.Render(viewContext, writer);
return writer.ToString();
}
}
#endregion
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With