这已经是系列 的第13篇了,实际上到现在为止您应该对BlogEngine.Net的整体设计有了一定的把握,对部分实现细节有了比较深刻的认识,在阅读 BlogEngine.Net时希望坚持到最后,并把握住宏观,深入到微观。本文将详细介绍BlogEngine.Net中的HttpHandlers与 HttpModules,主要说明它们要实现的功能以及如何使用,并对几个必要的HttpHandler或HttpModule进行比较细致的分析。
HttpHandler和HttpModule
对于HttpHandler和HttpModule我这里不想多说了, 因为关于它们讲解的文章实在太多太多了,大家可以在博客园的找找看中直接输入“HttpHandler和HttpModule”就可以找到。我的理解就是 一个HttpHandler要实现IHttpHandler接口,主要是对某个请求进行直接处理,一个HttpModule要实现IHttpModule 接口,主要是在HttpApplication的生命周期的事件中对请求和响应进行过滤。它们都可以在Web.config文件中进行配置。
BlogEngine.Net中的HttpHandler
BlogEngine.Net中的HttpHandler都在BlogEngine.Core.Web.HttpHandlers空间下(除了MetaWeblogHandler,已经讲过,这里就不包含它了),通过Web.config我们可以看到它们的映射关系:
HttpHandlers映射表
<httpHandlers>
<add verb="*" path="file.axd" type="BlogEngine.Core.Web.HttpHandlers.FileHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="image.axd" type="BlogEngine.Core.Web.HttpHandlers.ImageHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="syndication.axd" type="BlogEngine.Core.Web.HttpHandlers.SyndicationHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="sitemap.axd" type="BlogEngine.Core.Web.HttpHandlers.SiteMap, BlogEngine.Core" validate="false"/>
<add verb="*" path="trackback.axd" type="BlogEngine.Core.Web.HttpHandlers.TrackbackHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="pingback.axd" type="BlogEngine.Core.Web.HttpHandlers.PingbackHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="opensearch.axd" type="BlogEngine.Core.Web.HttpHandlers.OpenSearchHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="metaweblog.axd" type="BlogEngine.Core.API.MetaWeblog.MetaWeblogHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="rsd.axd" type="BlogEngine.Core.Web.HttpHandlers.RsdHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="css.axd" type="BlogEngine.Core.Web.HttpHandlers.CssHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="js.axd" type="BlogEngine.Core.Web.HttpHandlers.JavaScriptHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="rating.axd" type="BlogEngine.Core.Web.HttpHandlers.RatingHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="opml.axd" type="BlogEngine.Core.Web.HttpHandlers.OpmlHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="blogml.axd" type="BlogEngine.Core.Web.HttpHandlers.BlogMLExportHandler, BlogEngine.Core" validate="false"/>
<add verb="*" path="sioc.axd" type="BlogEngine.Core.Web.HttpHandlers.Sioc, BlogEngine.Core" validate="false"/>
<add verb="*" path="apml.axd" type="BlogEngine.Core.Web.HttpHandlers.Apml, BlogEngine.Core" validate="false"/>
<add verb="*" path="foaf*.axd" type="BlogEngine.Core.Web.HttpHandlers.Foaf, BlogEngine.Core" validate="false"/>
</httpHandlers>
下面对它们进行一一描述:
FileHandler:主要完成对于一些物理文件的请求,例如在文章中插入的文件资源的请求就由它来处理。它的实现比较简单,就直接去磁盘的文件保存目录中读取,我们只要留意一下SetContentType就行了。
ImageHandler:与FileHandler类似,不过它处理的是图片。与FileHandler分开的原因主要就是图片需要在页面上展现(ContentType必须给浏览器讲清楚或采用默认)。
SyndicationHandler:它比较复杂,主要完成各种订阅需求的处理,需要结合 SyndicationFormat(格式枚举)和SyndicationGenerator(按照SyndicationFormat的格式生成 XML)一起使用。我们可以看一下它的实现过程,首先根据请求的查询字符串生成标题,格式,列表(GenerateItemList使用了Search 等),使用CleanList进行了一个过滤,分页处理,SetHeader,最后使用SyndicationGenerator的WriteFeed进 行了输出。这里考虑的情况比较多,但是BlogEngine.Net使用的分割方法很好的解决了复杂的订阅问题,值得研究一下。
SiteMap:网站地图,可以给一些搜索引擎提供更好的搜索信息,以XML格式将一些文章等页面的链接输出。
TrackbackHandler:接收Trackback信息的处理,前文已经讲过,这里不多说了。
PingbackHandler:接收Pingback信息的处理,前文已经讲过,这里也不多说了。
OpenSearchHandler:提供公开搜索处理,前文已经讲过,这里也不多说了。
RsdHandler:这个处理器很有用,它完成将BlogEngine.Net中的一些公开的接口等信息发布出去,类似WebService的WSDL文件。
CssHandler:主要处理了页面中的Css文件的压缩,上文中讲述BlogBasePage中提及过。处理器的实现很值得关注,压缩可以使用gzip或deflate两种格式,
经典的压缩处理
1private const string GZIP = "gzip";
2private const string DEFLATE = "deflate";
3
4private static void Compress(HttpContext context)
5{
6 if (context.Request.UserAgent != null && context.Request.UserAgent.Contains("MSIE 6"))
7 return;
8
9 if (IsEncodingAccepted(GZIP))
10 {
11 context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
12 SetEncoding(GZIP);
13 }
14 else if (IsEncodingAccepted(DEFLATE))
15 {
16 context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress);
17 SetEncoding(DEFLATE);
18 }
19}
并提供了一些事件供外界Hookup处理。压缩之前去掉空格等字符,分为从本地获得文件和从远程使用WebClient下载文件。并使用了缓存。
JavaScriptHandler:主要处理页面中引用的脚本的压缩,与Css处理方式类似。注意StripWhitespace的处理与Css的不同。
RatingHandler:主要完成给Post打分的功能,记得上文中有部分涉及到,从PostViewBase中的Rating可以看出,这个请求实际上是通过Ajax发送过来的。
OpmlHandler:获得OPML列表文件,类似收藏文章的列表。
BlogMLExportHandler:BlogEngine.Net中的文章导出处理,以前的文章讲过。
Sioc、Foaf、Apml的注释都是“Based on John Dyer's (http://johndyer.name/) extension.”,这个应该是对于某个标准XML的输出吧,主要提供了本博客系统中的一些公开的信息。关于它们不做太多的研究,我想这并不影响我们 对BlogEngine.Net的研究。
实际上这些HttpHandlers的请求链接很多都是在BlogBasePage加入到Html的Head中的,或者给浏览器使用,或者给搜索引擎使用。
BlogEngine.Net中的HttpModule
BlogEngine.Net中的HttpModule都在BlogEngine.Core.Web.HttpModules空间下,通过Web.config我们可以看到它们的映射关系:
httpModules映射表
<httpModules>
<add name="WwwSubDomainModule" type="BlogEngine.Core.Web.HttpModules.WwwSubDomainModule, BlogEngine.Core"/>
<add name="UrlRewrite" type="BlogEngine.Core.Web.HttpModules.UrlRewrite, BlogEngine.Core"/>
<add name="CompressionModule" type="BlogEngine.Core.Web.HttpModules.CompressionModule, BlogEngine.Core"/>
<add name="ReferrerModule" type="BlogEngine.Core.Web.HttpModules.ReferrerModule, BlogEngine.Core"/>
<!–Remove the default ASP.NET modules we don't need–>
<remove name="PassportAuthentication"/>
<remove name="Profile"/>
<remove name="AnonymousIdentification"/>
</httpModules>
请注意它们的注册顺序,因为WwwSubDomainModule和UrlRewrite都Hookup了HttpApplication的BeginRequest事件。下面对它们进行一一描述:
WwwSubDomainModule:Hookup了HttpApplication的BeginRequest事件,主要是对请求的URL中的"www"的处理以得到绝对的链接。
UrlRewrite:Hookup了HttpApplication的BeginRequest事 件,对一些URL的重写,实际上WwwSubDomainModule和UrlRewrite都是在处理URL,我们在看BlogEngine.Net中 的源代码时多留意一下它的URL,包括Post中的各种链接的URL等,注意区分它们都是做什么用的。
ReferrerModule:主要是对于请求的Referrer进行统计,纪录这些请求都是从哪里发送来的,提供了相应的事件供外部Hookup,注意IsSpam的实现和对于纪录日志使用了新线程来异步完成。
CompressionModule:一个Page页面的压缩处理模块,同样根据请求来判断是使用 gzip还是deflate,对于页面中链接的WebResource也是用WebResourceFilter进行了过滤处 理,WebResourceFilter实现了Stream,
WebResourceFilter的Write方法
1public override void Write(byte[] buffer, int offset, int count)
2{
3 byte[] data = new byte[count];
4 Buffer.BlockCopy(buffer, offset, data, 0, count);
5 string html = System.Text.Encoding.Default.GetString(buffer);
6
7 Regex regex = new Regex("<script\\s*src=\"((?=[^\"]*webresource.axd)[^\"]*)\"\\s*type=\"text/JavaScript\"[^>]*>[^<]*(?:</script>)?", RegexOptions.IgnoreCase);
8 foreach (Match match in regex.Matches(html))
9 {
10 string relative = match.Groups[1].Value;
11 string absolute = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
12 html = html.Replace(relative, Utils.RelativeWebRoot + "js.axd?path=" + HttpUtility.UrlEncode(absolute + relative));
13 }
14
15 byte[] outdata = System.Text.Encoding.Default.GetBytes(html);
16 _sink.Write(outdata, 0, outdata.GetLength(0));
17}
在重写的Write中主要是将webresource.axd转交给了JavaScriptHandler处理以达到压缩的目的。注意它是在PreRequestHandlerExecute中Hookup的,都是给Response提供的流过滤器。
总结
由于篇幅所限,对于BlogEngine.Net中一些 HttpHandlers和HttpModules我并没有作更多的深入讨论,这里只是提供给大家一个学习指南,希望有所帮助。 BlogEngine.Net中大量使用了正则匹配,Url处理,压缩,缓存等都是比较通用的,值得我们关注与学习。
通用的处理方法值得我们去收藏
上一篇:BlogEngine.Net架构与源代码分析系列part12:页面共同的基类——BlogBasePage
返回到目录