This is possible. For partial views it's simpler, because you can capture the output of Html.Partial
directly, modifying it before the response is written to the output stream.
In order to do that, you'd create an extension method, which could look something like this:
public static class HtmlExtensions
{
public static HtmlString PartialIE9TableFix(this HtmlHelper helper,
string view, object model = null)
{
var partialOutput = helper.Partial(view, model).ToString();
partialOutput = Regex.Replace(partialOutput, @"/td>\s+<td",
"/td><td", RegexOptions.IgnoreCase);
return MvcHtmlString.Create(partialOutput);
}
}
As you can see, it's capturing the output of Html.Partial
directly, and then performing the replacement on that. You'd use it in your view like so:
@Html.PartialIE9TableFix("YourPartial")
However, to do this for actual views requires a lot more work, and certainly a lot more care when using it. In order to do this, we actually need to capture, and modify, the response stream before it's sent to the client.
The IE9TableFixFilter
below is very heavily based on code from Minify HTML with .NET MVC ActionFilter.
using System;
using System.IO;
using System.Text;
public class IE9TableFixFilter : Stream
{
public IE9TableFixFilter(Stream response, Func<string, string> filter)
{
this.response = response;
this.filter = filter;
}
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return true; } }
public override bool CanWrite { get { return true; } }
public override void Flush() { response.Flush(); }
public override long Length { get { return 0; } }
public override long Position { get; set; }
public override int Read(byte[] buffer, int offset, int count)
{
return response.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return response.Seek(offset, origin);
}
public override void SetLength(long value)
{
response.SetLength(value);
}
public override void Close()
{
response.Close();
}
public override void Write(byte[] buffer, int offset, int count)
{
//byte[] data = new byte[count];
//Buffer.BlockCopy(buffer, offset, data, 0, count);
string s = Encoding.Default.GetString(buffer);
s = filter(s);
byte[] outData = Encoding.Default.GetBytes(s);
response.Write(outData, 0, outData.GetLength(0));
}
private Stream response;
private Func<string, string> filter;
}
The majority of code here is filling in implementations for abstract
members of Stream
. The important part is what's going on in the Write
method.
The version of Write
from the article first makes a copy of the bytes of the stream without actually using them. There's no mention there if this is for some specific reason, but it seems useless to me, so I commented those lines out.
Next up, we need to create a simple ActionFilter
to apply the response filter to:
using System.Text.RegularExpressions;
using System.Web.Mvc;
public class IE9TableFixFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var response = filterContext.HttpContext.Response;
response.Filter = new IE9TableFixFilter(response.Filter,
s => Regex.Replace(s, @"/td>\s+<td", "/td><td", RegexOptions.IgnoreCase));
}
}
Now that's all done, I would strongly recommend that you don't apply this filter globally, instead choosing to decorate it on actions that require its use. The reason for that is because it will naturally incur some performance penalty, so it's best to be explicit about when it's actually needed. You won't need the partial extension method when using this. So simply decorate your actions to use it:
[IE9TableFixFilterAttribute]
public ActionResult Index()
{
return View();
}
Update
To make the filter attribute more efficient, you could actually just apply it to browsers that contain the user-agent string MSIE 9.0
:
public class IE9TableFixFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var response = filterContext.HttpContext.Response;
if (request.UserAgent.Contains("MSIE 9.0"))
{
response.Filter = new IE9TableFixFilter(response.Filter,
s => Regex.Replace(s, @"/td>\s+<td", "/td><td", RegexOptions.IgnoreCase));
}
}
}