Here is my first attempt at creating a custom view engine for ASP.NET MVC, I was inspired by http://www.worldofwarcraft.com/ which uses the XSL/XML combination with client side transformations. I would assume this saves them a copious amount of bandwidth. I am surprised this hasn’t been done more often which leads me to believe there must be some flaws with this approach. But nevertheless here is a View Engine that provides the ability to render out XML/XSLT combos as well as do the transforms server side.
Currently I do not have a reliable way of detecting client side XSLT support but this could be done via interrogating the User Agent or even a XSLT test page that sets a cookie. The real win with this approach I can see is the user downloads your visual elements once (the XSL style sheet) and the data can change numerous times. I will post a more detailed tutorial on how to write your own View Engine at a later date.
//Add this to your GlobalASAX protected void Application_Start() { ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new XsltViewEngine("~/Content/Views/")); RegisterRoutes(RouteTable.Routes); } public class XsltViewEngine : VirtualPathProviderViewEngine { public XsltViewEngine(string xsltHome) { ViewLocationFormats = new[] { xsltHome + "{1}/{0}.xslt", xsltHome + "Shared/{0}.xslt" }; PartialViewLocationFormats = ViewLocationFormats; } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return new XmlView(); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { if (ClientSideXsltSupported()) return new XsltView() { ViewPath = viewPath }; else return new XsltTransformView() { ViewPath = viewPath }; } private bool ClientSideXsltSupported() { //TODO: work out a way to detect. //controllerContext.HttpContext.Request.Cookies["XsltSupported"] return true; } } public class XmlView : IView { public void Render(ViewContext viewContext, System.IO.TextWriter writer) { var serializer = new XmlSerializer(viewContext.ViewData.Model.GetType()); var writerSettings = new XmlWriterSettings { OmitXmlDeclaration = true }; using (var xmlWriter = XmlWriter.Create(writer, writerSettings)) { if (xmlWriter != null) serializer.Serialize(xmlWriter, viewContext.ViewData.Model); } } } public class XsltView : IView { public string ViewPath { get; set; } public void Render(ViewContext viewContext, System.IO.TextWriter writer) { viewContext.HttpContext.Response.ContentType = "text/xml"; writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); writer.WriteLine("<?xml-stylesheet type=\"text/xsl\" href=\"" + VirtualPathUtility.ToAbsolute(ViewPath) + "\"?>"); var serializer = new XmlSerializer(viewContext.ViewData.Model.GetType()); var writerSettings = new XmlWriterSettings { OmitXmlDeclaration = true }; using (var xmlWriter = XmlWriter.Create(writer, writerSettings)) { if (xmlWriter != null) serializer.Serialize(xmlWriter, viewContext.ViewData.Model); } } } public class XsltTransformView : IView { public string ViewPath { get; set; } public void Render(ViewContext viewContext, System.IO.TextWriter writer) { var serializer = new XmlSerializer(viewContext.ViewData.Model.GetType()); using (var ms = new MemoryStream()) { using (var xmlWriter = XmlWriter.Create(ms)) { if (xmlWriter != null) serializer.Serialize(xmlWriter, viewContext.ViewData.Model); } ms.Seek(0, SeekOrigin.Begin); var transformer = new XslCompiledTransform(true); transformer.Load(viewContext.HttpContext.Server.MapPath(ViewPath)); transformer.Transform(XmlReader.Create(ms), null, writer); } } }
I don’t think that would work.
It does, try it out.
Interesting idea. this could be useful.
I like the idea and want to make use of it, simply because I think Xslt is a much better templating language than the Microsoft MVC views (in their current state).
Unfortunately, I’m having some issues with implementation. I’m trying to put these classes into a separate DLL. I’ve referenced System.Web.Mvc, System.Xml.Serialization and System.Xml. The project isn’t being built on this line:
viewContext.HttpContext.Response.ContentType = “text/xml”;
It’s telling me that Response isn’t a part of HttpContextBase (i.e. ViewContext.HttpContext). The same happens where it tries to get viewContext.HttpContext.Server. What am I missing?
Thanks.
Odhran.
Ah, ignore my previous post … System.Web.Abstractions was needed too.
Cheers!
I would just like to second Odhran’s comment about XSLT being better. I think the current “bee-sting” views are so messy and XSL is much more elegant and is IMO the natural choice for Views in MVC.
Great to see others exploring this route.
… of course, Steven Sanderson describes exactly this in his book
(page 358)