• ASP.Net 重写IHttpModule 来拦截 HttpApplication 实现HTML资源压缩和空白过滤


    务实直接上代码:

    1. 重写FilterModule.cs

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using System.Web;
     7 using System.Text.RegularExpressions;
     8 using System.IO.Compression;
     9 
    10 namespace Compress.FilterModule
    11 {
    12     public class FilterModule : IHttpModule
    13     {
    14         public void Dispose()
    15         {
    16             //
    17         }
    18 
    19         /// <summary>
    20         /// Init method is only used to register the desired event
    21         /// </summary>
    22         /// <param name="context"></param>
    23         public void Init(HttpApplication context)
    24         {
    25             context.BeginRequest += new EventHandler(context_BeginRequest);
    26             //context.EndRequest += new EventHandler(context_EndRequest);
    27             //context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
    28         }
    29 
    30 
    31         /// <summary>
    32         /// Handles the BeginRequest event of the context control.
    33         /// </summary>
    34         /// <param name="sender">The source of the event.</param>
    35         /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    36         void context_BeginRequest(object sender, EventArgs e)
    37         {
    38             HttpApplication app = sender as HttpApplication;
    39             HttpContext context = app.Context;
    40             if (context.CurrentHandler is System.Web.UI.Page)
    41             {
    42                 bool isPage = context.CurrentHandler.IsReusable;
    43             }
    44             if (app.Request.RawUrl.Contains(".aspx") || app.Request.RawUrl.EndsWith("/"))
    45             {
    46                 // HttpContext context = app.Context;
    47                 HttpRequest request = context.Request;
    48                 string acceptEncoding = request.Headers["Accept-Encoding"];
    49                 HttpResponse response = context.Response;
    50                 if (!string.IsNullOrEmpty(acceptEncoding))
    51                 {
    52                     acceptEncoding = acceptEncoding.ToUpperInvariant();
    53                     if (acceptEncoding.Contains("GZIP"))
    54                     {
    55                         //var straem = new GZipStream(response.Filter, CompressionMode.Compress);
    56                         response.Filter = new CompressWhitespaceFilter(response.Filter, CompressOptions.GZip);
    57                         response.AppendHeader("Content-encoding", "gzip");
    58                     }
    59                     else if (acceptEncoding.Contains("DEFLATE"))
    60                     {
    61                         response.Filter = new CompressWhitespaceFilter(context.Response.Filter, CompressOptions.Deflate);
    62                         response.AppendHeader("Content-encoding", "deflate");
    63                     }
    64                 }
    65                 response.Cache.VaryByHeaders["Accept-Encoding"] = true;
    66             }
    67         }
    68 
    69         // Test
    70         //void context_BeginRequest(object sender, EventArgs e)
    71         //{
    72         //    HttpApplication application = (HttpApplication)sender;
    73         //    HttpContext context = application.Context;
    74         //    context.Response.ContentType = "text/html";
    75         //    context.Response.Charset = "GB2312";
    76         //    context.Response.ContentEncoding = Encoding.Default;
    77         //    context.Response.Write("<h1 style='color:#00f'>Treatment from HttpModule,Begin...</h1><hr>");
    78         //}
    79 
    80         // Test
    81         //void context_EndRequest(object sender, EventArgs e)
    82         //{
    83         //    HttpApplication application = (HttpApplication)sender;
    84         //    HttpContext context = application.Context;
    85         //    context.Response.ContentType = "text/html";
    86         //    context.Response.Charset = "GB2312";
    87         //    context.Response.ContentEncoding = Encoding.Default;
    88         //    context.Response.Write("<hr><h1 style='color:#f00'>Treatment from HttpModule,End...</h1>");
    89         //}
    90 
    91     }
    92 }

    2. 处理压缩和匹配自定义过滤 CompressWhitespaceFilter.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Web;
      5 using System.IO;
      6 using System.Text;
      7 using System.Text.RegularExpressions;
      8 using System.IO.Compression;
      9 
     10 namespace Compress.ModuleDemo
     11 {
     12     public enum CompressOptions
     13     {
     14         GZip,
     15         Deflate,
     16         None
     17     }
     18 
     19     public class CompressWhitespaceFilter : Stream
     20     {
     21         StringBuilder responseHtml;
     22         const string _cssPattern = "(?<HTML><link[^>]*href\s*=\s*[\"\']?(?<HRef>[^"'>\s]*)[\"\']?[^>]*>)";
     23         const string _jsPattern = "(?<HTML><script[^>]*src\s*=\s*[\"\']?(?<SRC>[^"'>\s]*)[\"\']?[^>]*></script>)";
     24 
     25         private HttpApplication app;
     26         public HttpApplication App
     27         {
     28             get { return app; }
     29             set { app = value; }
     30         }
     31 
     32         private GZipStream _contentGZip;
     33         private DeflateStream _content_Deflate;
     34         private Stream _content;
     35         private CompressOptions _options;
     36         private bool disposed = false;
     37 
     38         private CompressWhitespaceFilter() { }
     39         public CompressWhitespaceFilter(Stream content, CompressOptions options)
     40         {
     41 
     42             responseHtml = new StringBuilder();
     43             if (options == CompressOptions.GZip)
     44             {
     45                 this._contentGZip = new GZipStream(content, CompressionMode.Compress, true);
     46                 this._content = this._contentGZip;
     47             }
     48             else if (options == CompressOptions.Deflate)
     49             {
     50                 this._content_Deflate = new DeflateStream(content, CompressionMode.Compress, true);
     51                 this._content = this._content_Deflate;
     52             }
     53             else
     54             {
     55                 this._content = content;
     56             }
     57             this._options = options;
     58         }
     59 
     60         public override bool CanRead
     61         {
     62             get { return this._content.CanRead; }
     63         }
     64 
     65         public override bool CanSeek
     66         {
     67             get { return this._content.CanSeek; }
     68         }
     69 
     70         public override bool CanWrite
     71         {
     72             get { return this._content.CanWrite; }
     73         }
     74 
     75         /// <summary>
     76         /// When overriding in a derived class, all buffers of the stream are cleared and all buffer data is written to the underlying device
     77         /// </summary>
     78         public override void Flush()
     79         {
     80             this._content.Flush();
     81             //Test
     82             //this._content.Dispose();
     83             //this._contentGZip.Dispose();
     84         }
     85 
     86 
     87         /// <summary>
     88         /// 重写Dispose方法,释放派生类自己的资源,并且调用基类的Dispose方法
     89         /// 使Gzip把缓存中余下的内容全部写入MemoryStream中,因为只有在Gzip流释放之后才能去承载对象中读取数据或判断数据大小
     90         /// </summary>
     91         /// <param name="disposing"></param>
     92         protected override void Dispose(bool disposing)
     93         {
     94             if (!this.disposed)
     95             {
     96                 try
     97                 {
     98                     if (disposing)
     99                     {
    100                         // Release the managed resources you added in this derived class here.
    101                         //xx.Dispose();
    102                     }
    103 
    104                     //  Release the native unmanaged resources you added in this derived class here.
    105                     //  xx.Close()
    106 
    107                     //if (_contentGZip != null)
    108                     //    _contentGZip.Close();
    109 
    110                     //if (_content_Deflate != null)
    111                     //    _content_Deflate.Close();
    112 
    113                     this._content.Close();
    114                     this.disposed = true;
    115                 }
    116                 finally
    117                 {
    118                     // Call Dispose on your base class.
    119                     base.Dispose(disposing);
    120                 }
    121             }
    122         }
    123 
    124         public override long Length
    125         {
    126             get { return this._content.Length; }
    127         }
    128 
    129         public override long Position
    130         {
    131             get
    132             {
    133                 return this._content.Position;
    134             }
    135             set
    136             {
    137                 this._content.Position = value;
    138             }
    139         }
    140 
    141         public override int Read(byte[] buffer, int offset, int count)
    142         {
    143             return this._content.Read(buffer, offset, count);
    144         }
    145 
    146         public override long Seek(long offset, SeekOrigin origin)
    147         {
    148             return this._content.Seek(offset, origin);
    149         }
    150 
    151         public override void SetLength(long value)
    152         {
    153             this._content.SetLength(value);
    154         }
    155 
    156         public override void Write(byte[] buffer, int offset, int count)
    157         {
    158             byte[] data = new byte[count + 1];
    159             Buffer.BlockCopy(buffer, offset, data, 0, count);
    160             string s = System.Text.Encoding.UTF8.GetString(data);
    161             s = Regex.Replace(s, "^\s*", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
    162             s = Regex.Replace(s, "\r\n", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
    163             s = Regex.Replace(s, "<!--*.*?-->", string.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
    164             byte[] outdata = System.Text.Encoding.UTF8.GetBytes(s);
    165             this._content.Write(outdata, 0, outdata.GetLength(0));
    166         }
    167 
    168 
    169         /// <summary>
    170         /// Replcase stylesheet links with ones pointing to HttpHandlers that compress and cache the css
    171         /// </summary>
    172         /// <param name="html"></param>
    173         /// <returns></returns>
    174         public string ReplaceCss(string html)
    175         {
    176             // create a list of the stylesheets
    177             List<string> stylesheets = new List<string>();
    178             // create a dictionary used for combining css in the same directory
    179             Dictionary<string, List<string>> css = new Dictionary<string, List<string>>();
    180 
    181             // create a base uri which will be used to get the uris to the css
    182             Uri baseUri = new Uri(app.Request.Url.AbsoluteUri);
    183 
    184             // loop through each match
    185             foreach (Match match in Regex.Matches(html, _cssPattern, RegexOptions.IgnoreCase))
    186             {
    187                 // this is the enire match and will be used to replace the link
    188                 string linkHtml = match.Groups[0].Value;
    189                 // this is the href of the link
    190                 string href = match.Groups[2].Value;
    191 
    192                 // get a uri from the base uri, this will resolve any relative and absolute links
    193                 Uri uri = new Uri(baseUri, href);
    194                 string file = "";
    195                 // check to see if it is a link to a local file
    196                 if (uri.Host == baseUri.Host)
    197                 {
    198                     // check to see if it is local to the application
    199                     if (uri.AbsolutePath.ToLower().StartsWith(app.Context.Request.ApplicationPath.ToLower()))
    200                     {
    201                         // this combines css files in the same directory into one file (actual combining done in HttpHandler)
    202                         int index = uri.AbsolutePath.LastIndexOf("/");
    203                         string path = uri.AbsolutePath.Substring(0, index + 1);
    204                         file = uri.AbsolutePath.Substring(index + 1);
    205                         if (!css.ContainsKey(path))
    206                             css.Add(path, new List<string>());
    207                         css[path].Add(file + (href.Contains("?") ? href.Substring(href.IndexOf("?")) : ""));
    208                         // replace the origianl links with blanks
    209                         html = html.Replace(linkHtml, "");
    210                         // continue to next link
    211                         continue;
    212                     }
    213                     else
    214                         file = uri.AbsolutePath + uri.Query;
    215                 }
    216                 else
    217                     file = uri.AbsoluteUri;
    218                 string newLinkHtml = linkHtml.Replace(href, "css.axd?files=" + file);
    219 
    220                 // just replace the link with the new link
    221                 html = html.Replace(linkHtml, newLinkHtml);
    222             }
    223 
    224             StringBuilder link = new StringBuilder();
    225             link.AppendLine("");
    226             foreach (string key in css.Keys)
    227             {
    228                 link.AppendLine(string.Format("<link href='{0}css.axd?files={1}' type='text/css' rel='stylesheet' />", key, string.Join(",", css[key].ToArray())));
    229 
    230             }
    231 
    232             // find the head tag and insert css in the head tag
    233             int x = html.IndexOf("<head");
    234             int num = 0;
    235             if (x > -1)
    236             {
    237                 num = html.Substring(x).IndexOf(">");
    238                 html = html.Insert(x + num + 1, link.ToString());
    239             }
    240             return html;
    241         }
    242 
    243         /// <summary>
    244         /// Replcase javascript links with ones pointing to HttpHandlers that compress and cache the javascript
    245         /// </summary>
    246         /// <param name="html"></param>
    247         /// <returns></returns>
    248         public string ReplaceJS(string html)
    249         {
    250             // if the javascript is in the head section of the html, then try to combine the javascript into one
    251             int start, end;
    252             if (html.Contains("<head") && html.Contains("</head>"))
    253             {
    254                 start = html.IndexOf("<head");
    255                 end = html.IndexOf("</head>");
    256                 string head = html.Substring(start, end - start);
    257 
    258                 head = ReplaceJSInHead(head);
    259 
    260                 html = html.Substring(0, start) + head + html.Substring(end);
    261             }
    262 
    263             // javascript that is referenced in the body is usually used to write content to the page via javascript, 
    264             // we don't want to combine these and place them in the header since it would cause problems
    265             // or it is a WebResource.axd or ScriptResource.axd
    266             if (html.Contains("<body") && html.Contains("</body>"))
    267             {
    268                 start = html.IndexOf("<body");
    269                 end = html.IndexOf("</body>");
    270                 string head = html.Substring(start, end - start);
    271 
    272                 head = ReplaceJSInBody(head);
    273 
    274                 html = html.Substring(0, start) + head + html.Substring(end);
    275             }
    276 
    277             return html;
    278         }
    279 
    280         /// <summary>
    281         /// Replaces the js in the head tag. (see ReplaceCss for comments)
    282         /// </summary>
    283         /// <param name="html"></param>
    284         /// <returns></returns>
    285         public string ReplaceJSInHead(string html)
    286         {
    287             List<string> javascript = new List<string>();
    288             Dictionary<string, List<string>> js = new Dictionary<string, List<string>>();
    289 
    290             Uri baseUri = new Uri(app.Request.Url.AbsoluteUri);
    291             foreach (Match match in Regex.Matches(html, _jsPattern, RegexOptions.IgnoreCase))
    292             {
    293                 string linkHtml = match.Groups[0].Value;
    294                 string src = match.Groups[2].Value;
    295 
    296                 Uri uri = new Uri(baseUri, src);
    297                 if (!Path.GetExtension(uri.AbsolutePath).Equals("js") && uri.AbsolutePath.Contains("WebResource.axd"))
    298                     continue;
    299                 if (uri.Host == baseUri.Host)
    300                 {
    301                     if (uri.AbsolutePath.ToLower().StartsWith(app.Context.Request.ApplicationPath.ToLower()))
    302                     {
    303                         int index = uri.AbsolutePath.LastIndexOf("/");
    304                         string path = uri.AbsolutePath.Substring(0, index + 1);
    305                         string file = uri.AbsolutePath.Substring(index + 1);
    306                         if (!js.ContainsKey(path))
    307                             js.Add(path, new List<string>());
    308                         js[path].Add(file + (src.Contains("?") ? src.Substring(src.IndexOf("?")) : ""));
    309                     }
    310                     else
    311                         javascript.Add(uri.AbsolutePath + uri.Query);
    312 
    313                 }
    314                 else
    315                     javascript.Add(uri.AbsoluteUri);
    316                 html = html.Replace(linkHtml, "");
    317             }
    318 
    319             int x = html.IndexOf("<head");
    320             int num = html.Substring(x).IndexOf(">");
    321             string link = "";
    322 
    323             foreach (string key in js.Keys)
    324             {
    325                 link = string.Format("<script src='{0}js.axd?files={1}' type='text/javascript' ></script>", key, string.Join(",", js[key].ToArray()));
    326                 html = html.Insert(x + num + 1, link + Environment.NewLine);
    327 
    328             }
    329             if (javascript.Count > 0)
    330             {
    331                 link = string.Format("<script src='js.axd?files={0}' type='text/javascript' /></script>", string.Join(",", javascript.ToArray()));
    332                 html = html.Insert(x + num + 1, link + Environment.NewLine);
    333             }
    334             return html;
    335         }
    336 
    337         /// <summary>
    338         /// Replaces the js in the body. (see ReplaceCss for comments)
    339         /// </summary>
    340         /// <param name="html"></param>
    341         /// <returns></returns>
    342         public string ReplaceJSInBody(string html)
    343         {
    344             Uri baseUri = new Uri(app.Request.Url.AbsoluteUri);
    345             foreach (Match match in Regex.Matches(html, _jsPattern, RegexOptions.IgnoreCase))
    346             {
    347                 string linkHtml = match.Groups[0].Value;
    348                 string src = match.Groups[2].Value;
    349 
    350 
    351                 Uri uri = new Uri(baseUri, src);
    352                 if (!uri.AbsolutePath.EndsWith(".js") && !uri.AbsolutePath.Contains("WebResource.axd"))
    353                     continue;
    354                 string file = "";
    355                 string path = "";
    356                 if (uri.Host == baseUri.Host)
    357                 {
    358                     if (uri.AbsolutePath.ToLower().StartsWith(app.Context.Request.ApplicationPath.ToLower()))
    359                     {
    360                         int index = uri.AbsolutePath.LastIndexOf("/");
    361                         path = uri.AbsolutePath.Substring(0, index + 1);
    362                         file = uri.AbsolutePath.Substring(index + 1) + (src.Contains("?") ? src.Substring(src.IndexOf("?")) : "");
    363                     }
    364                     else
    365                         file = uri.AbsolutePath + uri.Query;
    366                 }
    367                 else
    368                     file = uri.AbsoluteUri;
    369                 string newLinkHtml = linkHtml.Replace(src, path + "js.axd?files=" + file);
    370                 html = html.Replace(linkHtml, newLinkHtml);
    371             }
    372             return html;
    373         }
    374 
    375     }
    376 }

    在这里需要注意的是对GZIP 的释放,否则流数据会读取不到:

     1         /// <summary>
     2         /// 重写Dispose方法,释放派生类自己的资源,并且调用基类的Dispose方法
     3         /// 使Gzip把缓存中余下的内容全部写入MemoryStream中,因为只有在Gzip流释放之后才能去承载对象中读取数据或判断数据大小
     4         /// </summary>
     5         /// <param name="disposing"></param>
     6         protected override void Dispose(bool disposing)
     7         {
     8             if (!this.disposed)
     9             {
    10                 try
    11                 {
    12                     if (disposing)
    13                     {
    14                         // Release the managed resources you added in this derived class here.
    15                         //xx.Dispose();
    16                     }
    17 
    18                     //  Release the native unmanaged resources you added in this derived class here.
    19                     //  xx.Close()
    20 
    21                     //if (_contentGZip != null)
    22                     //    _contentGZip.Close();
    23 
    24                     //if (_content_Deflate != null)
    25                     //    _content_Deflate.Close();
    26 
    27                     this._content.Close();
    28                     this.disposed = true;
    29                 }
    30                 finally
    31                 {
    32                     // Call Dispose on your base class.
    33                     base.Dispose(disposing);
    34                 }
    35             }
    36         }

    对于C#非托管资源释放(Finalize/Dispose)方法理解:

    http://www.cnblogs.com/lzhdim/archive/2009/11/04/1595845.html

  • 相关阅读:
    在JavaWeb中使用Log4j步骤
    关于我为什么不再更新博客园了
    windows termial 配置文件
    windows10 命令行修复系统引导
    vscode修改code runner插件默认使用的编译器
    windows下vscode修复c++找不到头文件
    windows下安装mingw-w64
    vscode美化方法以及定制主题插件
    windows下隐藏磁盘分区
    2018 icpc 徐州网络赛 F Features Track
  • 原文地址:https://www.cnblogs.com/mschen/p/5904153.html
Copyright © 2020-2023  润新知