DotText源码阅读(7) --Pingback/TrackBack
DotText源码阅读(7) --Pingback/TrackBack 博客这种服务的区别于论坛和所谓文集网站,很大程度上我认为是由于pingback/trackback的存在,使得博客这种自媒体有可以延展加入SNS的要素。所以分析博客程序,我们需要了解这种协议以及协议的实施细节。 在dottext的源代码中,在发表作品中,我们可以看到有pingback协议的支持,同时在web services的实现中,有trackback协议的实现。至于什么是piongback/trackback协议,google下应当可以找到,也不用我费口舌。 通过 <HttpHandlerpattern='/(?:admin)'type='Dottext.Web.UI.Handlers.BlogExistingPageHandler, Dottext.Web'handlerType='Factory'/>的映射,使得我们访问每一个blog的admin目录时候,都会UrlRewrite到dottexweb\admin目录下的相对应aspx文件(参考前面部分),其中在发表post的时候,我们看到是这样一个调用关系: private void UpdatePost() { if(Page.IsValid) { string successMessage = Constants.RES_SUCCESSNEW; try { Entry entry = new Entry(EntryType); entry.Title = txbTitle.Text; entry.Body = Globals.StripRTB(ftbBody.Text,Request.Url.Host); … entry.BlogID = Config.CurrentBlog(Context).BlogID; if (PostID > 0) {//是更新操作 successMessage = Constants.RES_SUCCESSEDIT; entry.DateUpdated = DateTime.Now;//BlogTime.CurrentBloggerTime; entry.EntryID = PostID; … Entries.Update(entry); … } else {//新建操作 entry.DateCreated = DateTime.Now;//BlogTime.CurrentBloggerTime; PostID = Entries.Create(entry); }… } catch(Exception ex) {… } finally { … } } } Entries.Create(entry);是这样的: public static int Create(Entry entry, int[] CategoryIDs) { HandlerManager.PreCommit(entry,ProcessAction.Insert); int result = DTOProvider.Instance().Create(entry,CategoryIDs); if(result > 0) { HandlerManager.PostCommit(entry,ProcessAction.Insert); } return result; } 最终的数据存储试调用DTOProvider也就是DataDTOProvider 最终是落到 SqlDataProvider 来实现数据存储操作。但是我们注意到 HandlerManager.PostCommit(entry,ProcessAction.Insert); 这个操作。仔细看看: HandlerManager 是一个关于Entry操作类的包装类(wapper class),PreCommit是这样定义的: Process(ProcessState.PreCommit,e,pa); 而Process是这样读取web.config的 public static void Process(ProcessState ps, Entry e, ProcessAction pa) { //Do we have factories? 在疑惑是否该用工厂模式呢 EntryHandler[] hanlers = Config.Settings.EntryHandlers; //这是反序列化哦,这里的Config是Dottext.Framework.Configuration.Config if(e != null && hanlers != null) { //walk the entries 遍历全部处理例程 for(int i = 0; i<hanlers.Length; i++) { EntryHandler handler = hanlers[i]; if(ShouldProcess(ps,handler,e.PostType,pa)) { IEntryFactoryHandler ihandler = handler.IEntryFactoryHandlerInstance; //Call the IEntryFactoryHandler configure method. This gives async items a chance to 'ready' themselves //before leaving the main thread and entering the managed queue. ihandler.Configure(); if(handler.IsAsync) {//Add factory to managed queue. EntryHanlderQueue.Enqueue(ihandler,e); } else { ihandler.Process(e); } } } } }ShouldProcess 是判断是预提交还是已经提交post,决定是否应该进行handler的实例化,如果是已经提交的Post,我们需要进行handler.IEntryFactoryHandlerInstance; IentryFactoryHandlerInstance最终是通过ihandler = (IEntryFactoryHandler)Activator.CreateInstance(Type.GetType(this.ItemType));来实例化数组元素的().经过实例化后,就可以执行了。此时根据 handler.IsAsync 的属性,决定是允许 EntryHanlderQueue.Enqueue(ihandler,e); 加入队列,还是马上处理ihandler.Process(e);.对于可以异步执行的静态函数 Enque 处理:public static void Enqueue(IEntryFactoryHandler factory, Entry e) { EntryHanlderQueue ehq = new EntryHanlderQueue(factory,e); ManagedThreadPool.QueueUserWorkItem(new WaitCallback(ehq.Enqueue)); }构造一个实例,然后加入线程队列进行任务排队。线程管理暂不讨论。我们看看这几个EntryHandler.TrackBack Handler是如何处理的呢?public void Process(Dottext.Framework.Components.Entry e) { //Get a list of links from the current post StringCollection links = TrackHelpers.GetLinks(e.Body); if(links != null && links.Count > 0) { //Instantiate our proxy TrackBackNotificationProxy proxy = new TrackBackNotificationProxy(); //Walk the links for(int i = 0; i<links.Count; i++) { string link = links[i]; //get the page text string pageText = BlogRequest.GetPageText(link,e.Link); if(pageText != null) { try { string desc = null; if(e.HasDescription) { desc = e.Description; } else { desc=string.Format('TrackBack From:{0}',e.Link); } desc = regexStripHTML.Replace(e.Body,string.Empty); if(desc.Length > 100) { int place = 100; int len = desc.Length-1; while(!Char.IsWhiteSpace(desc[place]) && i < len) { place++; } desc = string.Format('{0}...',desc.Substring(0,place)); } } //attempt a trackback. proxy.TrackBackPing(pageText,link,e.Title,e.Link,e.Author,desc); } catch(Exception ex) { Logger.LogManager.CreateExceptionLog(ex,string.Format('Trackback Failure: {0}',link)); } } } } } TrackHelpers.GetLinks 会分析Entry.Body字符串,获得post的全部href连结,也就是对外引用部分,这个TrackBack利用proxy.TrackBackPing(pageText,link,e.Title,e.Link,e.Author,desc); 将本文的对外引用通告刚刚获得的连接地址。 TrackBackPing : string pageText = BlogRequest.GetPageText(link,e.Link);会利用BlogRequest的http协议能力下载被引用地址的source code,然后 link为另外blog的地址,而e.Link为reffer,这是为了告知对方那个页面引用了link。经过安全解码后,获得了link的源代码,然后TrackBackPing会进行分析,找寻string sPattern = @'<rdf:\w+\s[^>]*?>(</rdf:rdf>)?';匹配的部分,分析出其中的引用通告地址。下一步就是利用SendPing(string trackBackItem, string parameters),向目标地址处post一个application/x-www-form-urlencoded'的数据。此即完成了一次trackBack. 其他几个EntryHandler也是分同步和异步的,大家可以照此阅读。 题外话:那些没有礼貌的实现pingback/Trackback的所谓blog,就不要妄自称自己为博客服务商(BSP)吧。