Receiving Trackbacks by ThinqLinq

Receiving Trackbacks

If you've been following along, I've been working on enhancing this site a bit recently. A couple of the most recent enhancements can be found in the following posts:

Continuing in this tradition, I wanted to include the ability to be notified when other sites post links to my posts. There are several such API's that support this kind of notification, including Trackbacks and Pingbacks. In this post, we'll look at receiving Trackbacks and saving them to our database (using LINQ of course).

The technical specification for Trackbacks is hosted at http://www.movabletype.org/documentation/developer/callbacks/ . To implement the listener part of the trackback, we simply need to be able to accept a HTTP Post which includes the post id in the URI and the trackback's details as form parameters. For example, if I wanted to send a trackback for my Paging with AJAX post, I would issue the following request:

POST http://ThinqLinq.com/Trackback.aspx?id=22040
Content-Type: application/x-www.form-urlencoded; charset=utf-8

title=Test+Postback&url=http://www.fansite.com/&excerpt=Good+post&blog_name=Fansite

In this request, the URI specifies the name of the trackback server including the ID of the post we are tracking: http://ThinqLinq.com/Trackback.aspx?id=22040. The trackback API specifies that the Content-Type should always be application/x-www.form-urlencoded; charset=utf-8. The body specifies the values we want the trackback server to know about. Each of these are optional. In this case, we want to send the server a trackback for a post called "Test Postback" which can be found at http://www.fansite.com on the blog that is named "Fansite". To be nice, we'll include an excerpt of our post with the value of "Good post". Because the content type needs to be urlencoded, we need to make sure that each value is encoded properly. To summarize, following parameter values would be sent for our test request:

  • title=Test Postback
  • url=http://www.fansite.com/
  • excerpt=Good Post
  • blog_name=Fansite

One tool to issue a raw HTTP request to test this is Fiddler. Inside Fiddler, we can select the Request Builder tab. Select "POST" as the method and enter the URI that we wish to post to (http://ThinqLinq.com/Trackback.aspx?id=22040). We then set the content type in the Request Headers and our post content in the Request Body. Below is a screen shot to send this request using Fiddler.

TrackbackFiddler

We'll discuss what we need to do to send this request from code in a later post. For now, we'll focus on receiving this request. While I wanted to do this with WCF, implementing it as a standard Web page is the easiest. As a result, we'll create a page called Trackback.aspx. Since there is no real UI on this, we'll remove everything from the page except for the pointer to the page's code file:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Trackback.aspx.vb" Inherits="Trackback" %>

On the page load, we need to clear our buffer because we're going to just send a simple XML response indicating any errors we may experience.

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Response.Buffer = True
        Response.Clear()
        Response.Cache.SetCacheability(HttpCacheability.NoCache)
        Response.ContentType = "text/xml"

Next, we need to create the object that we want to persist in our database. We already have a Comment table which maps to our Comments. In many ways Trackbacks are like Comments, so we'll use the same table. Since they do have different behaviors, we'll use LINQ to SQL's ability to support inheritance. Thus we'll add a discriminator column on our Comment table called CommentType and default the value to "C" since most of the items in that table will be comments.

Next, in our DBML we need to indicate the new mapping. Our existing comment class will now be an abstract base class called CommentBase. We'll change it's Inheritance Modifier to MustInherit as a result. We'll then add three derived types for Comment, Trackback, and Pingback to model the behaviors we'll be implementing now. We'll also need to make sure to Inheritance Discriminator values of each of the inheritance arrows. When we're done, this piece of our model now looks as follows:

TrackbackDbml

Now that we have our new Trackback type, we can instantiate it on our Page_Load with the form values that we get from our page's request fields.

      Dim trackback As New LinqBlog.BO.Trackback
      Dim id As Integer
      If Not Integer.TryParse(Request("id"), id) Then
          Return CreateFailureMessage("Id invalid")
      End If

      trackback.PostId = ID
      trackback.Creator = If(Request.Params("blog_name"), "")
      trackback.CreatorEmail = String.Empty
      trackback.Description = _
          String.Format("Trackback from &lt;a href={0}{1}{0}&gt;{2}&lt;/a&gt", _
                        ControlChars.Quote, _
                        If(Request.Params("url"), ""), _
                        HttpUtility.UrlDecode(If(Request.Params("title"), "")))
      trackback.EnteredDate = Now
      trackback.CreatorLink = If(Request.Params("url"), "")

We do need to decode the Url encoded values. We also need to watch out for missing values because each of the form values are optional. In this case, we'll use VB 9's support for ternary If. Also notice here that we aren't setting the CommentType field. Since we are using the discriminator column and set that in LINQ to SQL's metadata, when we save Trackback objects, it will automatically assign the type to "T" for us.

Now that we have our trackback object, adding it to the database with LINQ is trivial. However, we do need to make one additional check before saving the trackback. We don't want to save multiple trackbacks for a single post. Thus we'll check to see if there are any trackbacks for this post in the database already before saving this one:

            Using dc As NewLinqBlogDataContext
                'Make sure we don't already have a comment for this post from this url
               
If(Aggregatetb Indc.CommentBases.OfType(OfTrackback)() _
                   Wheretb.PostId = id And_
                        tb.CreatorLink = values.Url _
                    IntoCount()) = 0 Then

                   
'Add it
                   
dc.CommentBases.InsertOnSubmit(trackback)
                    dc.SubmitChanges()

                End If
            End Using

We're almost done. The last step is to send the appropriate response to the sending system as required by the Trackback API specification. This is a simple XML response containing any error messages. With XML Literals, this is a simple copy paste operation from the Trackback API:

 

    Private Function CreateSuccessMessage() As String
        Return <?xml version="1.0" encoding="utf-8"?>
               <response>
                   <error>0</error>
               </response>.ToString()
    End Function

    Private Function CreateFailureMessage(ByVal description As String) As String
        Return <?xml version="1.0" encoding="utf-8"?>
               <response>
                   <error>1</error>
                   <message><%= description %></message>
               </response>.ToString()
    End Function

With this in place, we just add the following inside of our If statement after we save the value to the database:

    Response.Write(CreateSuccessMessage)
    Response.End()

If there were any issues in creating or saving our trackback, we can send the Response.Write(CreateFailureMessage(error)).

Feel free to test this by tracking back to this post. Be aware that I am moderating the trackbacks to avoid comment spam that can be inevitable when exposing public known API's like this. Please don't abuse the ability to send trackbacks as I don't want to have to remove the functionality.

Posted on - Comment
Categories: VB - VB Dev Center - LINQ - SEO -
comments powered by Disqus