Trackbacks are a form of linkback a way of notify a site that your site has made a reference to it. Trackbacks are slowly being deprecated in favor of pingbacks. The spec can be found at Six Apart.
To provide trackback support we need to do 3 things:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ProcessRequest()
{
LinkBack linkBack = new LinkBack { ID = Guid.NewGuid(), CreationDate = DateTime.Now };
//Retreave routing information
string controler = Url.RequestContext.RouteData.Values["trackBackController"].ToString();
string action = Url.RequestContext.RouteData.Values["trackBackAction"].ToString();
string id = Url.RequestContext.RouteData.Values["trackBackID"].ToString();
HttpRequestBase request = Url.RequestContext.HttpContext.Request;
HttpResponseBase response = Url.RequestContext.HttpContext.Response;
response.Buffer = false;
response.Clear();
response.ContentType = "application/xml";
//Extract data from HTTP request
if (!string.IsNullOrEmpty(request["url"]))
{
linkBack.LinkingPostUrl = request["url"];
linkBack.Title = request["url"] + "@" + DateTime.Now.ToLongTimeString();
}
else
{
generateErrorResponse(1, "URL is missing", response.Output);
}
if (!string.IsNullOrEmpty(request["blog_name"]))
linkBack.LinkingSiteName = request["blog_name"];
if (!string.IsNullOrEmpty(request["title"]))
linkBack.LinkingPostTitle = request["title"];
if (!string.IsNullOrEmpty(request["excerpt"]))
linkBack.LinkingPostExcerpt = request["excerpt"];
linkBack.Save();
generateSuccessResponse(response.Output,
id,
new Uri("http://www.rrreese.com/" + controler + "/" + action + "/" + id),
"",
"en-US");
return new EmptyResult();
}
The ProcessRequest action (part of the LinkBack Controller), processes HTTP POSTs and saves the trackback data to the LinkBack model (in this case a linkback class, the implementation of which is not relevant here). The action first creates a new model, extracts the routing data then extracts the trackback parameters from the HTTP request before saving them to back to the model. If the action is successful a success response is sent otherwise an error. the title, blog_name, title and excerpt are all optional parameters however the url parameter is required.
private static void generateSuccessResponse(TextWriter textWriter,
string title,
Uri link,
string description,
string language)
{
XElement xml =
new XElement("response",
new XElement("error", "0"),
new XElement("rss",
new XAttribute("version", "0.91"),
new XElement("channel",
new XElement("title", title),
new XElement("link", link.AbsolutePath),
new XElement("description", description),
new XElement("language", language))));
writeXmlToStream(textWriter, xml);
}
Successful trackback requests must return the following XML in their response:
<?xml version="1.0" encoding="utf-8"?> <response> <error>0</error> </response>
Errors are generated as follows:
private static void generateErrorResponse(int number, string message, TextWriter textWriter)
{
string element = (number > 0) ? "error" : "message";
string elementValue = (number > 0) ? number.ToString() : message;
XElement xml = new XElement("response",
new XElement(element, elementValue));
writeXmlToStream(textWriter, xml);
}
private static void writeXmlToStream(TextWriter textWriter, XElement xml)
{
//Turn off Byte Order Mark (BOM)
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings { Encoding = new UTF8Encoding(false) };
using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, xmlWriterSettings))
{
xml.WriteTo(xmlWriter);
xmlWriter.Flush();
}
}
An unsuccessful request must return the following XML:
<?xml version="1.0" encoding="utf-8"?> <response> <error>1</error> <message>The error message</message> </response>
Note that in writeXmlToStream() we ensure that the Byte Order Mark is disabled to ensure that it does not cause a parsing error.
Having created an appropriate Controller and Action we now need to hook them up to be routed correctly. Inside the Global.asax file, inside RegisterRoutes() we need to add the following:
routes.MapRoute
(
"Trackback",
"LinkBack/ProcessRequest/{trackBackController}/{trackBackAction}/{trackBackID}",
new
{
controller = "LinkBack",
action = "ProcessRequest",
trackBackController = "",
trackBackAction = "",
trackBackID = ""
}
);
The first parameter is the name, the second is the pattern to match, and the third the anonymous type that contains the route data. See MVC URL Routing for more details. Note that trackBackController,trackBackAction and trackBackID are used inside the ProcessRequest() Action.
Our final task is to add a link to the trackback in our web page. This is a simple matter of generating an appropriate link, this page for example has a track back of http://www.rrreese.com/LinkBack/ProcessRequest/Artice/Show/ASP.NET MVC TrackBack Implementation. The specification states that auto discover can be used, basically a block of XML is embedded in the page so that the trackback URL can be found programmaticly. We generate the XML:
XNamespace rdf = "rdf"; XNamespace dc = "dc"; XNamespace trackback = "trackback"; XElement xml = new XElement(rdf + "RDF", new XAttribute(XNamespace.Xmlns + "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), new XAttribute(XNamespace.Xmlns + "dc", "http://purl.org/dc/elements/1.1/"), new XAttribute(XNamespace.Xmlns + "trackback", "http://madskills.com/public/xml/rss/module/trackback/"), new XElement(rdf + "Description", new XAttribute(rdf + "about", "http://www.rrreese.com/Article/Show/" + article.Title), new XAttribute(dc + "identifier", "http://www.rrreese.com/Article/Show/" + article.Title), new XAttribute(dc + "title", article.Title), new XAttribute(trackback + "ping", "http://www.rrreese.com/LinkBack/ProcessRequest/Artice/Show/" + article.Title)));
Which produces the following:
<!-- <RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns="rdf"> <Description rdf:about="http://www.rrreese.com/Article/Show/ASP.NET MVC TrackBack Implementation" dc:identifier="http://www.rrreese.com/Article/Show/ASP.NET MVC TrackBack Implementation" dc:title="ASP.NET MVC TrackBack Implementation" trackback:ping="http://www.rrreese.com/LinkBack/ProcessRequest/Artice/Show/ASP.NET MVC TrackBack Implementation" /> </RDF> -->
Note that the XML is enclosed in quotes to help HTML validators out. So what is the work flow for this system? Firstly a user on a website creates a new post linking to a page on your site. Their site either queries your page and extracts the trackback URL, or is provided the track back URL by the user. Their site sends and HTTP POST to your site, MVC receives the request and routes it to the ProcessRequest action in the LinkBack controller. ProcessRequest extracts the trackback data and saves it to the model.
Finally test trackbacks can be sent using either Simpletracks or Trackback Test Form at the RSS Blog(Internet Explorer only) to confirm your system is operating correctly.