using System; using System.Web; using System.Web.Mvc; using System.IO; using System.Web.Routing; using Westwind.Web.Mvc.Properties; namespace Westwind.Web.Mvc { /// /// Class that renders MVC views to a string using the /// standard MVC View Engine to render the view. /// /// Requires that ASP.NET HttpContext is present to /// work, but works outside of the context of MVC /// public class ViewRenderer { /// /// Required Controller Context /// protected ControllerContext Context { get; set; } /// /// Initializes the ViewRenderer with a Context. /// /// /// If you are running within the context of an ASP.NET MVC request pass in /// the controller's context. /// Only leave out the context if no context is otherwise available. /// public ViewRenderer(ControllerContext controllerContext = null) { // Create a known controller from HttpContext if no context is passed if (controllerContext == null) { if (HttpContext.Current != null) controllerContext = CreateController().ControllerContext; else throw new InvalidOperationException( "ViewRenderer must run in the context of an ASP.NET " + "Application and requires HttpContext.Current to be present."); } Context = controllerContext; } /// /// Renders a full MVC view to a string. Will render with the full MVC /// View engine including running _ViewStart and merging into _Layout /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to render the view with /// String of the rendered view or null on error public string RenderViewToString(string viewPath, object model = null) { return RenderViewToStringInternal(viewPath, model, false); } /// /// Renders a full MVC view to a writer. Will render with the full MVC /// View engine including running _ViewStart and merging into _Layout /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to render the view with /// String of the rendered view or null on error public void RenderView(string viewPath, object model, TextWriter writer) { RenderViewToWriterInternal(viewPath, writer, model, false); } /// /// Renders a partial MVC view to string. Use this method to render /// a partial view that doesn't merge with _Layout and doesn't fire /// _ViewStart. /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// String of the rendered view or null on error public string RenderPartialViewToString(string viewPath, object model = null) { return RenderViewToStringInternal(viewPath, model, true); } /// /// Renders a partial MVC view to given Writer. Use this method to render /// a partial view that doesn't merge with _Layout and doesn't fire /// _ViewStart. /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// Writer to render the view to public void RenderPartialView(string viewPath, object model, TextWriter writer) { RenderViewToWriterInternal(viewPath, writer, model, true); } /// /// Renders a full MVC view to a writer. Will render with the full MVC /// View engine including running _ViewStart and merging into _Layout /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// Active Controller context /// String of the rendered view or null on error public static string RenderView(string viewPath, object model = null, ControllerContext controllerContext = null) { ViewRenderer renderer = new ViewRenderer(controllerContext); return renderer.RenderViewToString(viewPath, model); } /// /// Renders a full MVC view to a writer. Will render with the full MVC /// View engine including running _ViewStart and merging into _Layout /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// Writer to render the view to /// Active Controller context /// String of the rendered view or null on error public static void RenderView(string viewPath, TextWriter writer, object model, ControllerContext controllerContext) { ViewRenderer renderer = new ViewRenderer(controllerContext); renderer.RenderView(viewPath, model, writer); } /// /// Renders a full MVC view to a writer. Will render with the full MVC /// View engine including running _ViewStart and merging into _Layout /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// Active Controller context /// optional out parameter that captures an error message instead of throwing /// String of the rendered view or null on error public static string RenderView(string viewPath, object model, ControllerContext controllerContext, out string errorMessage) { errorMessage = null; try { ViewRenderer renderer = new ViewRenderer(controllerContext); return renderer.RenderViewToString(viewPath, model); } catch (Exception ex) { errorMessage = ex.GetBaseException().Message; } return null; } /// /// Renders a full MVC view to a writer. Will render with the full MVC /// View engine including running _ViewStart and merging into _Layout /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// Active Controller context /// Writer to render the view to /// optional out parameter that captures an error message instead of throwing /// String of the rendered view or null on error public static void RenderView(string viewPath, object model, TextWriter writer, ControllerContext controllerContext, out string errorMessage) { errorMessage = null; try { ViewRenderer renderer = new ViewRenderer(controllerContext); renderer.RenderView(viewPath, model, writer); } catch (Exception ex) { errorMessage = ex.GetBaseException().Message; } } /// /// Renders a partial MVC view to string. Use this method to render /// a partial view that doesn't merge with _Layout and doesn't fire /// _ViewStart. /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// Active controller context /// String of the rendered view or null on error public static string RenderPartialView(string viewPath, object model = null, ControllerContext controllerContext = null) { ViewRenderer renderer = new ViewRenderer(controllerContext); return renderer.RenderPartialViewToString(viewPath, model); } /// /// Renders a partial MVC view to string. Use this method to render /// a partial view that doesn't merge with _Layout and doesn't fire /// _ViewStart. /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// The model to pass to the viewRenderer /// Active controller context /// Text writer to render view to /// optional output parameter to receive an error message on failure public static void RenderPartialView(string viewPath, TextWriter writer, object model = null, ControllerContext controllerContext = null) { ViewRenderer renderer = new ViewRenderer(controllerContext); renderer.RenderPartialView(viewPath, model, writer); } /// /// Internal method that handles rendering of either partial or /// or full views. /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// Model to render the view with /// Determines whether to render a full or partial view /// Text writer to render view to protected void RenderViewToWriterInternal(string viewPath, TextWriter writer, object model = null, bool partial = false) { // first find the ViewEngine for this view ViewEngineResult viewEngineResult = null; if (partial) viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath); else viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null); if (viewEngineResult == null) throw new FileNotFoundException(); // get the view and attach the model to view data var view = viewEngineResult.View; Context.Controller.ViewData.Model = model; var ctx = new ViewContext(Context, view, Context.Controller.ViewData, Context.Controller.TempData, writer); view.Render(ctx, writer); } /// /// Internal method that handles rendering of either partial or /// or full views. /// /// /// The path to the view to render. Either in same controller, shared by /// name or as fully qualified ~/ path including extension /// /// Model to render the view with /// Determines whether to render a full or partial view /// String of the rendered view private string RenderViewToStringInternal(string viewPath, object model, bool partial = false) { // first find the ViewEngine for this view ViewEngineResult viewEngineResult = null; if (partial) viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath); else viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null); if (viewEngineResult == null || viewEngineResult.View == null) throw new FileNotFoundException(Resources.ViewCouldNotBeFound); // get the view and attach the model to view data var view = viewEngineResult.View; Context.Controller.ViewData.Model = model; string result = null; using (var sw = new StringWriter()) { var ctx = new ViewContext(Context, view, Context.Controller.ViewData, Context.Controller.TempData, sw); view.Render(ctx, sw); result = sw.ToString(); } return result; } /// /// Creates an instance of an MVC controller from scratch /// when no existing ControllerContext is present /// /// Type of the controller to create /// Controller for T /// thrown if HttpContext not available public static T CreateController(RouteData routeData = null, params object[] parameters) where T : Controller, new() { // create a disconnected controller instance T controller = (T) Activator.CreateInstance(typeof (T), parameters); // get context wrapper from HttpContext if available HttpContextBase wrapper = null; if (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; } } /// /// Empty MVC Controller instance used to /// instantiate and provide a new ControllerContext /// for the ViewRenderer /// public class EmptyController : Controller { } }