Minify HTML output from an ASP.Net MVC Application

前端 未结 5 1132
余生分开走
余生分开走 2020-12-24 01:36

This is likely a duplicate of the below question but the only answer is a dead link:
Minify Html output of ASP.NET Application

When working with ASP.Net one of t

5条回答
  •  北荒
    北荒 (楼主)
    2020-12-24 02:00

    This is an old question, but I'll toss in my solution in case it benefits someone else.

    I had a "minification" filter using regular expressions that worked for the most part. It failed when it came to perserving whitespace in pre and textarea tags. I ended up hitting a wall a few days ago because of it so I spent about three days reading through what others have tried and trying out my ideas. In the end I settled on parsing the HTML using the HtmlAgilityPack and removing the whitespace nodes from there. Because whitespace in pre and textarea elements was not considered whitespace by HAP it worked out in my favor and did exactly what I wanted. I did have trouble in the beginning because the HTML was being sent in chunks, but I resolved it by buffering it until it was complete. Here's my code in case it's beneficial to someone else.

    Do note that this filter works for me in my application (ASP.NET MVC 5). Ideally, minification should be done during publishing to avoid the need for filters like this. Lastly, @naivists in his answer states that GZIP compressing the response will have a better effect than minification, but I slightly disagree with him. Yes, it will, but minification does reduce the response ever so slightly on top of that. Where it really shines is when styling with CSS because now you don't have to worry about whitespace bumping and misplacing elements and having to use margin/padding/positioning trickery to correct it.

    [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() != "")).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);
        }
    }
    

提交回复
热议问题