这是一个老问题,但我会抛出我的解决方案,以防它对其他人有益。
我有一个使用大部分工作的正则表达式的“缩小”过滤器。pre
在保留和textarea
标记中的空白方面失败了。几天前我最终因为它碰壁了,所以我花了大约三天时间阅读其他人的尝试并尝试我的想法。最后,我决定使用 HtmlAgilityPack 解析 HTML 并从那里删除空白节点。因为pre
和textarea
元素中的空白不被 HAP 视为空白,所以它对我有利,并且完全符合我的要求。一开始我确实遇到了麻烦,因为 HTML 是分块发送的,但我通过缓冲它直到它完成来解决它。这是我的代码,以防它对其他人有益。
请注意,此过滤器适用于我的应用程序(ASP.NET MVC 5)。理想情况下,应该在发布期间进行缩小以避免需要像这样的过滤器。最后,@naivists 在他的回答中指出,GZIP 压缩响应比缩小效果更好,但我有点不同意他的观点。是的,它会,但缩小确实会在此之上略微降低响应。它真正闪耀的地方是使用 CSS 进行样式设置,因为现在您不必担心空白碰撞和元素放错位置,也不必使用边距/填充/定位技巧来纠正它。
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
internal sealed class MinifyHtmlAttribute :
ActionFilterAttribute {
public override void OnActionExecuted(
ActionExecutedContext filterContext) {
if (filterContext == null
|| filterContext.IsChildAction) {
return;
}
filterContext.HttpContext.Response.Filter = new MinifyHtmlStream(filterContext.HttpContext);
}
}
internal sealed class MinifyHtmlStream :
MemoryStream {
private readonly MemoryStream BufferStream;
private readonly HttpContextBase Context;
private readonly Stream FilterStream;
public MinifyHtmlStream(
HttpContextBase httpContextBase) {
BufferStream = new MemoryStream();
Context = httpContextBase;
FilterStream = httpContextBase.Response.Filter;
}
public override void Flush() {
BufferStream.Seek(0, SeekOrigin.Begin);
if (Context.Response.ContentType != "text/html") {
BufferStream.CopyTo(FilterStream);
return;
}
var document = new HtmlDocument();
document.Load(BufferStream);
var spans = document.DocumentNode.Descendants().Where(
d =>
d.NodeType == HtmlNodeType.Element
&& d.Name == "span").SelectMany(
d => d.ChildNodes.Where(
cn => cn.NodeType == HtmlNodeType.Text)).ToList();
// Some spans have content that needs to be trimmed.
foreach (var span in spans) {
span.InnerHtml = span.InnerHtml.Trim();
}
var nodes = document.DocumentNode.Descendants().Where(
d =>
(d.NodeType == HtmlNodeType.Text
&& d.InnerText.Trim().Length == 0)
|| (d.NodeType == HtmlNodeType.Comment
&& d.InnerText.Trim() != "<!DOCTYPE html>")).Select(
d => d).ToList();
foreach (var node in nodes) {
node.Remove();
}
document.Save(FilterStream);
}
public override void Write(
byte[] buffer,
int offset,
int count) {
BufferStream.Write(buffer, offset, count);
}
}