用BlogEngine的代码扒了下Trackback的代码直接搬到了Librazyframe上,结果根本跑不起来:
System.Net.WebException: 请求被中止: 请求已被取消。 —> System.IO.IOException: 在写入所有字节之前不能关闭流。
在写入所有字节之前不能关闭流?好像用到流的只有
1 2 3 4 |
using (var writer = new StreamWriter(request.GetRequestStream())) { writer.Write(message.ToString()); } |
整了半天,决定跑单步调试。根据 BlogEngine.Net架构与源代码分析系列part7:Web2.0特性——Pingback&Trackback找到了入口点SendPing.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
/// <summary> /// Pings all the ping services specified on the /// PingServices admin page and send track- and pingbacks /// </summary> [Extension("Pings all the ping services specified on the PingServices admin page and send track- and pingbacks", "1.3", "BlogEngine.NET")] public class SendPings { #region Constructors and Destructors /// <summary> /// Initializes static members of the <see cref="SendPings"/> class. /// Hooks up an event handler to the Post.Saved event. /// </summary> static SendPings() { Post.Saved += PostSaved; Page.Saved += PostSaved; } #endregion #region Methods /// <summary> /// Executes the pings from the new thread. /// </summary> /// <param name="item"> /// The publishable item. /// </param> /// <param name="itemUrl"> /// The item Url. /// </param> private static void Ping(IPublishable item, Uri itemUrl) { try { Thread.Sleep(2000); // Ping the specified ping services. PingService.Send(itemUrl); // Send trackbacks and pingbacks. if (!BlogSettings.Instance.EnableTrackBackSend && !BlogSettings.Instance.EnablePingBackSend) { return; } if (item.Content.ToUpperInvariant().Contains("\"HTTP")) { Manager.Send(item, itemUrl); } } catch (Exception) { // We need to catch this exception so the application doesn't get killed. } } /// <summary> /// Handles the Saved event of the Post control. /// Sends the pings in a new thread. /// <remarks> /// It opens a new thread and executes the pings from there, /// because it takes some time to complete. /// </remarks> /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="BlogEngine.Core.SavedEventArgs"/> instance containing the event data.</param> private static void PostSaved(object sender, SavedEventArgs e) { if (e.Action == SaveAction.None || e.Action == SaveAction.Delete) { return; } var item = (IPublishable)sender; if (!item.IsVisibleToPublic) { return; } var url = item.AbsoluteLink; ThreadPool.QueueUserWorkItem(state => Ping(item, url)); } #endregion } |
“实际上SendPings类是BlogEngine.Net的一个Extension(类具有Extension特性,这个后面会有专门的一篇文章来讲解),它监听了Page.Saved和Post.Saved”
PostSaved监听Page.Saved和Post.Saved,当并非删除、无动作或不公开时,异步执行Ping(item, url)
Ping方法睡两秒(为什么?),执行PingService.Send(itemUrl);和Manager.Send(item, itemUrl);
这就是真正的入口了。
f12到Manager.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public static void Send(IPublishable item, Uri itemUrl) { foreach (var url in GetUrlsFromContent(item.Content)) { var trackbackSent = false; if (BlogSettings.Instance.EnableTrackBackSend) { // ignoreRemoteDownloadSettings should be set to true // for backwards compatibilty with Utils.DownloadWebPage. var remoteFile = new RemoteFile(url, true); var pageContent = remoteFile.GetFileAsString(); // ReadFromWeb(url); var trackbackUrl = GetTrackBackUrlFromPage(pageContent); if (trackbackUrl != null) { var message = new TrackbackMessage(item, trackbackUrl, itemUrl); trackbackSent = Trackback.Send(message); } } if (!trackbackSent && BlogSettings.Instance.EnablePingBackSend) { Pingback.Send(itemUrl, url); } } } |
首先尝试发送Trackback,未成功则发送Pingback。不过GetTrackBackUrlFromPage好像不识别WordPress的rel="trackback",只认Trackback:Ping
继续F12,到了Trackback.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/// <summary> /// The send. /// </summary> /// <param name="message"> /// </param> /// <returns> /// The send. /// </returns> public static bool Send(TrackbackMessage message) { if (!BlogSettings.Instance.EnableTrackBackSend) { return false; } if (message == null) { throw new ArgumentNullException("message"); } OnSending(message.UrlToNotifyTrackback); // Warning:next line if for local debugging porpuse please donot remove it until you need to // tMessage.PostURL = new Uri("http://www.artinsoft.com/webmaster/trackback.html"); var request = (HttpWebRequest)WebRequest.Create(message.UrlToNotifyTrackback); // HttpHelper.CreateRequest(trackBackItem); request.Credentials = CredentialCache.DefaultNetworkCredentials; request.Method = "POST"; request.ContentLength = message.ToString().Length; request.ContentType = "application/x-www-form-urlencoded"; request.KeepAlive = false; request.Timeout = 10000; using (var writer = new StreamWriter(request.GetRequestStream())) { writer.Write(message.ToString()); } bool result; HttpWebResponse response; try { response = (HttpWebResponse)request.GetResponse(); OnSent(message.UrlToNotifyTrackback); string answer; using (var sr = new StreamReader(response.GetResponseStream())) { answer = sr.ReadToEnd(); } // TODO: This could be a strict XML parsing if necesary/maybe logging activity here too result = response.StatusCode == HttpStatusCode.OK && answer.Contains("<error>0</error>"); } catch { // (WebException wex) result = false; } return result; } |
30行完全把中文置于不顾,这就是开头的异常产生的原因。(行号以所贴代码计算)
修改一下,开始单步
注释以下几行
SendPing.cs 38,41 Thread.Sleep(2000); PingService.Send(itemUrl);
修改Trackback.cs
1 2 3 4 5 6 7 8 9 10 |
//using ( var writer = new StreamWriter(request.GetRequestStream())) //{ // writer.Write(message.ToString()); //} var chars = message.ToString().ToCharArray(); byte[] sarr = System.Text.Encoding.UTF8.GetBytes(chars); var len = sarr.Length; request.ContentLength = len; var writer = request.GetRequestStream(); writer.Write(sarr, 0, len); |
1 2 3 4 5 6 7 8 |
下断: SendPing.cs 84 ThreadPool.QueueUserWorkItem(state => Ping(item, url)); Manager.cs 01 public static void Send(IPublishable item, Uri itemUrl) Trackback.cs 09 public static bool Send(TrackbackMessage message) 之后F11,喝杯茶(生成慢了点),发布篇文章(要带Url锚文本),Visual Studio马上把焦点抢去 (开始后才知道SendPing.cs 38的睡两秒原来是避免异步冲突,害得我在代码间转来转去) 在Manager.cs 12处大有超时危险……还好我测试用网址速度可以。 F11,F11……总算到了目标Trackback.cs |
1 |
1 |
var request = (HttpWebRequest)WebRequest.Create(message.UrlToNotifyTrackback); |
发起请求了
1 2 3 4 5 |
request.Credentials = CredentialCache.DefaultNetworkCredentials; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded;charset=UTF-8"; request.KeepAlive = false; request.Timeout = 10000; |
一堆设置,应注意的是application/x-www-form-urlencoded这是,Trackback的格式
1 2 3 4 5 6 |
var chars = message.ToString().ToCharArray(); byte[] sarr = System.Text.Encoding.UTF8.GetBytes(chars); var len = sarr.Length; request.ContentLength = len; var writer = request.GetRequestStream(); writer.Write(sarr, 0, len); |
这是我修改后的写入,真正的核心代码实际上就只有message.ToString(),进去看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/// <summary> /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>. /// </summary> /// <returns> /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>. /// </returns> public override string ToString() { return string.Format( CultureInfo.InvariantCulture, "title={0}&url={1}&excerpt={2}&blog_name={3}", this.Title, this.PostUrl, this.Excerpt, this.BlogName); } |
真正我们要发送的就是“title=xx&url=xx&excerpt=xx&blog_name=xx”,
title – 文章的标题
excerpt – 文章的摘要。在Movable Type系统中,如果摘录信息超过255个字符将会被截断为252个字符,并在后面增加…三个字符
url – 文章的永久连接。象其它永久连接一样,这个连接应可能准确地在页面中定位文章的入口,因有疑问时这个链接会用到
blog_name – 发表文章的blog的名称
而这些参数,是在Manager.cs 17传入的(BlogName是BlogSettings.Instance.Name)
把这个字符串转化为byte[]后写入请求,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
bool result; HttpWebResponse response; try { response = (HttpWebResponse)request.GetResponse(); OnSent(message.UrlToNotifyTrackback); string answer; using (var sr = new StreamReader(response.GetResponseStream())) { answer = sr.ReadToEnd(); } // TODO: This could be a strict XML parsing if necesary/maybe logging activity here too result = response.StatusCode == HttpStatusCode.OK && answer.Contains("<error>0</error>"); } catch { // (WebException wex) result = false; } return result; |
1 |
获取响应(看来更改没错,测试网址马上接到了Trackback)。如果成功,响应会包含"<error>0</error>",否则失败。出了异常更不用说了。
返回后到了Manager.cs 22,如果trackbackSent为false,则发送Pingback
Pingback实现和Trackback大同小异,只不过是用了Xmlrpc而已
F5放水,结束了单步,看来BlogEngine也有挺多毛病的……不能自动发现WordPress的Trackback URL,Trackback消息不能带中文,发送Trackback或Pingback时可能无法捕获超时异常……不过BusinessBase这种状态维护方式还是挺不错的。