ActionFilterAttribute의 HtmlFilter, HTML 코드 압축

13260 단어 attribute
이 필터를 켜면 최종적으로 생성된 HTML 코드가 압축됩니다. 데이터가 많은 사이트에서 대역폭 비용을 줄일 수 있다면 조금 줄일 것입니다. 왜 기꺼이 하지 않겠습니까?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using Mkt.Common;

namespace Mkt.Mvc.Filter
{
    public class HtmlFilter : ActionFilterAttribute
    {

        #region = IsAvailable =

        private bool _isavailable = true;

        public bool IsAvailable
        {
            get { return _isavailable; }
            set { _isavailable = value; }
        }

        #endregion
        public HtmlFilter() { }
        public HtmlFilter(bool isAvailable)
        {
            _isavailable = isAvailable;
        }
        public string SetGray(string text)
        {
            text = Common.HtmlHelper.Compress(text);
            if (DateTime.Now.Month == 4 && DateTime.Now.Day == 21)
            {
                text = Regex.Replace(text, "</head>", @"<style type=""text/css"">html {filter:gray;}</style></head>
", RegexOptions.IgnoreCase);
            }
            return text;
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
        }
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);
            if (!IsAvailable) return;
#if DEBUG
            return;
#endif
            filterContext.RequestContext.HttpContext.Response.Filter = new HtmlStreamFilter(filterContext.RequestContext.HttpContext.Response.Filter, filterContext.RequestContext.HttpContext.Response.ContentEncoding, SetGray);
        }
    }
}

compress 코드:
/// <summary>
        ///   html  
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
        public static string Compress(string text)
        {
            text = Regex.Replace(text, @"<!--\S*?-->", string.Empty);
            text = Regex.Replace(text, @"^\s+|\s+{1}quot;, string.Empty);
            text = Regex.Replace(text, "
", " "); text = Regex.Replace(text, @">\s+?<", "><"); text = Regex.Replace(text, @"\s{2,}", " "); text = Regex.Replace(text, " {2,}", @"\s"); text = Regex.Replace(text, @"\s{2,}", @"\s"); return text; }

그중의 SetGray는 4.21 애도일이 회색으로 변하는 설정이다.
배합CompressFilter하면 효과가 더 좋아요~
이전에 HtmlStreamFilter 소스가 다음과 같이 노출되었습니다.
public class HtmlStreamFilter : Stream
    {
        Stream responseStream;
        long position;
        StringBuilder responseHtml;
        #region = CurrentEncoding =

        private Encoding _currentencoding;

        public Encoding CurrentEncoding
        {
            get { return _currentencoding; }
            set { _currentencoding = value; }
        }

        #endregion
        Func<string, string> _func;
        public HtmlStreamFilter(Stream inputStream, Encoding enc, Func<string, string> func
)
        {
            responseStream = inputStream;
            _currentencoding = enc;
            _func = func;
            responseHtml = new StringBuilder();
        }

        #region Filter overrides
        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override void Close()
        {
            responseStream.Close();
        }

        public override void Flush()
        {
            responseStream.Flush();
        }

        public override long Length
        {
            get { return 0; }
        }

        public override long Position
        {
            get { return position; }
            set { position = value; }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return responseStream.Seek(offset, origin);
        }

        public override void SetLength(long length)
        {
            responseStream.SetLength(length);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return responseStream.Read(buffer, offset, count);
        }
        #endregion

        #region Dirty work
        public override void Write(byte[] buffer, int offset, int count)
        {
            string strBuffer = CurrentEncoding.GetString(buffer, offset, count);
            #region =    HTML  ,    =

            var bof = new Regex("<html", RegexOptions.IgnoreCase);
            if (!bof.IsMatch(responseHtml.ToString()))
            {
                responseStream.Write(buffer, offset, count);
                return;
            }
            #endregion
            // ---------------------------------
            // Wait for the closing </html> tag
            // ---------------------------------
            Regex eof = new Regex("</html>", RegexOptions.IgnoreCase);

            if (!eof.IsMatch(strBuffer))
            {
                responseHtml.Append(strBuffer);
            }
            else
            {
                responseHtml.Append(strBuffer);
                string finalHtml = responseHtml.ToString();

                finalHtml = _func(finalHtml);

                // Transform the response and write it back out

                byte[] data = CurrentEncoding.GetBytes(finalHtml);

                responseStream.Write(data, 0, data.Length);
            }
        }
        #endregion

    }

두 번째 방법, 사용.net 내부 클래스 구현:
public class HtmlFilter : ActionFilterAttribute
    {

        #region = IsAvailable =

        private bool _isavailable = true;

        public bool IsAvailable
        {
            get { return _isavailable; }
            set { _isavailable = value; }
        }

        #endregion
        private TextWriter _originalWriter;
        private static readonly MethodInfo SwitchWriterMethod = typeof(HttpResponse).GetMethod("SwitchWriter", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        public HtmlFilter() { }
        public HtmlFilter(bool isAvailable)
        {
            _isavailable = isAvailable;
        }
        public string SetGray(string text)
        {
            text = Common.HtmlHelper.Compress(text);
            if (DateTime.Now.Month == 4 && DateTime.Now.Day == 21)
            {
                text = Regex.Replace(text, "</head>", @"<style type=""text/css"">html {filter:gray;}</style></head>
", RegexOptions.IgnoreCase);
            }
            return text;
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!IsAvailable) return;
#if DEBUG
            return;
#endif
            _originalWriter = (TextWriter)SwitchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { new HtmlTextWriter(new StringWriter()) });
            base.OnActionExecuting(filterContext);
        }
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);
            if (!IsAvailable) return;
#if DEBUG
            return;
#endif

            HtmlTextWriter cacheWriter = (HtmlTextWriter)SwitchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { _originalWriter });
            string textWritten = cacheWriter.InnerWriter.ToString();
            if (filterContext.HttpContext.Response.ContentType == "text/html")
            {
                textWritten = Compress(textWritten).ToString();
            }
            filterContext.HttpContext.Response.Write(textWritten);
            //filterContext.RequestContext.HttpContext.Response.Filter = new HtmlStreamFilter(filterContext.RequestContext.HttpContext.Response.Filter, filterContext.RequestContext.HttpContext.Response.ContentEncoding, SetGray);
        }
        private static StringBuilder Compress(string text)
        {
            StringBuilder str = new StringBuilder();
            StringBuilder strlink = new StringBuilder();
            var s = new char[] { '\f', '
', '\r', '\t', '\v' }; Func<int, object> P = c => null; Func<int, object> Ptag = c => null; // Func<int, object> Pcomment = c => null; // #region - - Func<int, object> state = P = i => { char c = text[i]; if (c == '<') // < Ptag { if (i + 4 < text.Length) { if (text.Substring(i + 1, 3) == "!--") // { return Pcomment; } } str.Append(c); return Ptag; } else if (s.Contains(c) == true) { return P; } else if (c == ' ') { if (i + 1 < text.Length) { if (s.Union(new char[] { ' ', '<' }).Contains(text[i + 1]) == false) { str.Append(c); } } return P; } else { str.Append(c); return P; } }; #endregion #region - Tag - Ptag = i => { char c = text[i]; if (c == '>') // p { str.Append(c); return P; } else if (s.Contains(c) == true) { return Ptag; } else if (c == ' ') { if (i + 1 < text.Length) { if (new char[] { ' ', '/', '=', '>' }.Contains(text[i + 1]) == false) { str.Append(c); } } return Ptag; } else { str.Append(c); return Ptag; } }; #endregion #region - - Pcomment = i => { char c = text[i]; if (c == '>' && text.Substring(i - 2, 3) == "-->") { return P; } else { return Pcomment; } }; #endregion for (int index = 0; index < text.Length; index++) { state = (Func<int, object>)state(index); } return str; } }

이 방법에서 새로운compress 방법으로 사용한
GoodSpeed의 성과.

좋은 웹페이지 즐겨찾기