隐藏

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

发布:2021/7/13 16:37:22作者:管理员 来源:本站 浏览次数:994

务实直接上代码:


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