首页 > 代码库 > ASP.Net 重写IHttpModule 来拦截 HttpApplication 实现HTML资源压缩和空白过滤

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.FilterModule11 {12     public class FilterModule : IHttpModule13     {14         public void Dispose()15         {16             //17         }18 19         /// <summary>20         /// Init method is only used to register the desired event21         /// </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         // Test70         //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         // Test81         //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                 finally117                 {118                     // Call Dispose on your base class.119                     base.Dispose(disposing);120                 }121             }122         }123 124         public override long Length125         {126             get { return this._content.Length; }127         }128 129         public override long Position130         {131             get132             {133                 return this._content.Position;134             }135             set136             {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 = http://www.mamicode.com/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 =http://www.mamicode.com/ 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 css171         /// </summary>172         /// <param name="html"></param>173         /// <returns></returns>174         public string ReplaceCss(string html)175         {176             // create a list of the stylesheets177             List<string> stylesheets = new List<string>();178             // create a dictionary used for combining css in the same directory179             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 css182             Uri baseUri = new Uri(app.Request.Url.AbsoluteUri);183 184             // loop through each match185             foreach (Match match in Regex.Matches(html, _cssPattern, RegexOptions.IgnoreCase))186             {187                 // this is the enire match and will be used to replace the link188                 string linkHtml = match.Groups[0].Value;189                 // this is the href of the link190                 string href = http://www.mamicode.com/match.Groups[2].Value;191 192                 // get a uri from the base uri, this will resolve any relative and absolute links193                 Uri uri = new Uri(baseUri, href);194                 string file = "";195                 // check to see if it is a link to a local file196                 if (uri.Host == baseUri.Host)197                 {198                     // check to see if it is local to the application199                     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 blanks209                         html = html.Replace(linkHtml, "");210                         // continue to next link211                         continue;212                     }213                     else214                         file = uri.AbsolutePath + uri.Query;215                 }216                 else217                     file = uri.AbsoluteUri;218                 string newLinkHtml = linkHtml.Replace(href, "css.axd?files=" + file);219 220                 // just replace the link with the new link221                 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=http://www.mamicode.com/‘{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 tag233             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 javascript245         /// </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 one251             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 problems265             // or it is a WebResource.axd or ScriptResource.axd266             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 = http://www.mamicode.com/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                     else311                         javascript.Add(uri.AbsolutePath + uri.Query);312 313                 }314                 else315                     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=http://www.mamicode.com/‘{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=http://www.mamicode.com/‘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 = http://www.mamicode.com/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                     else365                         file = uri.AbsolutePath + uri.Query;366                 }367                 else368                     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                 try11                 {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                 finally31                 {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

 

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