First of all, I never thought a problem so simple would give me such a hard time!
My Problem
I’m using Razor views to generate some XML. I know I should probably use some sort of Serializer/Deserializer for that kind of job, but I needed something easier to change and preferably something that doesn’t need to be compiled. The problem is an empty line before my content which off course makes it invalid.
The code looks like:
@{
Response.ContentType = "text/xml";
}
<?xml version="1.0" encoding="UTF-8" ?>
...
and here is how the output is generated:
First Try
The first thing I tried was to move the <xml> tag to the line above getting rid of the new lines that could possibly have.
@{
Response.ContentType = "text/xml";
}<?xml version="1.1" encoding="UTF-8" ?>
...
I even moved the Razor block to the bottom…
<?xml version="1.1" encoding="UTF-8" ?>
...
@{
Response.ContentType = "text/xml";
}
Nothing!
Second Try
Ok. This first line is not in my code. Something is putting it there. No problem! I can create a Response Filter and change the output removing this empty line.
using System.IO;
using System.Text;
using System.Web.Mvc;
namespace MyWebsite.Helpers
{
public class RemoveEmptyLineWriter : MemoryStream
{
private readonly Stream filter;
public RemoveEmptyLineWriter(Stream filter)
{
this.filter = filter;
}
public override void Write(byte[] buffer, int offset, int count)
{
var content = Encoding.UTF8.GetString(buffer);
content = RemoveFirstEmptyLine(content);
filter.Write(Encoding.UTF8.GetBytes(content), offset, Encoding.UTF8.GetByteCount(content));
}
private static string RemoveFirstEmptyLine(string content)
{
var firstLineIsEmpty = content.Substring(0, 2) == "\r\n";
return firstLineIsEmpty ? content.Substring(2, content.Length - 2) : content;
}
}
public class EmptyLinesRemoverFilter : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
var response = filterContext.HttpContext.Response;
response.Filter = new RemoveEmptyLineWriter(response.Filter);
}
}
}
[EmptyLinesRemoverFilter]
public ActionResult Index()
{
return View();
}
This code I found here, and then I changed it a little bit.
It simply adds to the Response a Filter which is invoked after the output is generated. So I can get it, modify and write again to the buffer.
I think this would have worked just fine if it wasn’t for my IIS being configured to Gzip the document. With Gzip enabled, after I read the buffer the content is just a bunch of messy characters. Decompressing the content and then compressing again is not a solution as it would cause an unnecessary overhead in my application. I’d rather turn Gzip off for those type of files but only if I really had to.
I also tried changing the order that my filter is added by changing the OnResultExecuted to OnActionExecuting but it doesn’t affect the order that the filter is executed, only the order in which it’s added to the response, which makes no difference.
My next try would be creating an IHttpModule, but I wanted something easier and something that I didn’t have to intercept all my requests.
Solution 1
Maybe you were lucky and the previous solutions worked for you, but in case they don’t, here is what worked for me. I had to go deeper in MVC and change the hierarchy of which the rendering is executed.
I ended up with this:
using System.Web.Mvc;
using System.Web.WebPages;
namespace MyWebsite.Helpers
{
public class RazorXmlViewPage<T> : WebViewPage<T>
{
public override void ExecutePageHierarchy()
{
Response.ContentType = "text/xml";
Layout = null;
PushContext(new WebPageContext(), Response.Output); // push a new page context to the stack
base.ExecutePageHierarchy();
}
public override void Execute()
{
}
}
public class RazorXmlViewPage : RazorXmlViewPage<object>
{
}
}
and in my Razor view…
@inherits RazorXmlViewPage<IEnumerable<string>>
<?xml version="1.0" encoding="UTF-8"?>
Thanks to this post that provided a source code that helped me get to this one.
The Razor view file inherits the WebViewPage by default. This page tells Razor that it inherits from a different class, in which I can override the execution of its core methods. We also have to override the Execute method because it is abstract.
It turns out I din’t even have to modify the output. Just by pushing a new WebPageContext to the stack the page rendered without the blank line.
But, if wanted, I could do some cools things with the Output. Take this code for instance:
public override void ExecutePageHierarchy()
{
StringWriter writer = new StringWriter();
PushContext(PageContext, writer);
base.ExecutePageHierarchy();
PopContext();
Response.Clear();
string output = writer.ToString();
output = output.Replace("<body>", "<body><h1>An Example of Filtering Razor Output</h1>");
Response.Write(output);
Response.End();
}
If you want to understand better the methods used in the code above, take a look at the WebViewPage source code.
Solution 2
Another solution is to Render your View as a string, using the method bellow, change it and set the Response to your new content.
public string RenderRazorViewToString(string viewName, object model)
{
ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
You know when you use return View(model);
. This method it’s actually an alias to return new ViewResult {}
. What the code above is doing is using the same implementation of that class, as you can see in the MVC source, in the method ExecuteResult.
I knew that I could use this code when dealing with this problem, but as I was looking at that class, I felt bad copying that much code from the MVC source and pasting it into my code. What if MVC updates as it does often and change that code? I’ll have to copy and paste it again in case this bug remains not fixed. So, for my use case I used the first solution as is less code copied and rewrote, plus, I didn’t even have to remove the first line from my output since the change skipped the bug. For other use cases the second solution might suit better your need/taste.
Please comment here what worked for you.