首页 > 代码库 > 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资源压缩和空白过滤
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。