Это старый вопрос, но я добавлю свое решение на случай, если оно принесет пользу кому-то еще.
У меня был фильтр минификации с использованием регулярных выражений, который работал по большей части. Сбой при сохранении пробелов в тегах pre
и textarea
. Я закончил тем, что ударил стену несколько дней назад из-за этого, поэтому я потратил около трех дней, читая то, что другие пробовали, и испытывая мои идеи. В конце я остановился на анализе HTML-кода с помощью HtmlAgilityPack и удалении из него узлов с пробелами. Поскольку пробелы в элементах 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);
}
}