advertisement

Building an RSS feed made simple

posted by Jeff
Discuss this article   Printer friendly

Those little orange XML icons are everywhere. How much will people think you suck if your site doesn't have one? Syndicate your content today or be the target of jokes at your next party.

RSS stands for "Really Simple Syndication," and it's all the rage. The idea is pretty simple. Output some XML to the Web that content aggregators and other sites can consume and display. If you have a normal routine of sites you visit with frequent content updates (news, blogs, whatever), and each site has an RSS feed, you can at a glance see what's new at all of these sites with an aggregator.

I won't go into detail about how to use these feeds, but there's a great little piece over at LockerGnome that tells you all about it. You'll also find links to aggregators, and if you find one you like, you'll find yourself subscribing to feeds all over the place.

The benefit to you is that you might just attract visits to your site from people who might not otherwise go there because they can see what's new at many sites without having to wade through all of their bookmarks. And hey, if you're putting out some kind of list of headlines on your site, you probably already have the data access code written. Now you just need to present it as XML.

XML you care about

Be honest, unless you're in a corporate environment writing code that moves data around between different systems, you probably don't care all that much about XML and you're tired of hearing Bill Gates say it will save the world. You'll be happy to know, however, that this is one of those situations where two different systems are sharing data, the first system being your Web site and the second being a content aggregator or another site, or whatever.

In case you've been living under a rock, XML stands for Extensible Markup Language, a nifty way to present data in a format that humans can sort of read and machines can consume. It's basically just self-describing data that looks a lot like HTML, only you need to follow a careful structure. An RSS feed is an XML document that has a bunch of content data.

The RSS specification

While you can use XML to pretty much present any data in any way, naturally our fancy little content aggregators and the like are going to expect that it be in a certain format. So before we look at the specification, let's first look at a spot of XML from this very site's NewsWire feed:

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
	<channel>
		<title>uberASP.Net NewsWire</title>
		<link>http://www.uberasp.net/newswire.aspx</link>
		<description>The latest headlines and articles from the world of ASP.NET, Microsoft's Web development platform.</description>
		<copyright>(c) 2004, POP World Media, LLC. All rights reserved.</copyright>
		<ttl>5</ttl>
		<item>
			<title>Using HttpModules to send the right data format to the client</title>
			<description>The same data might need to be presented in many different formats, including HTML, XML, rich text, or even 
			an Excel file. Through the use of HttpModules, your site will serve the right kind of data every time.</description>
			<link>http://www.uberasp.net/GetArticle.aspx?id=15</link>
			<pubDate>Mon, 12 Jan 2004 04:48:34 GMT</pubDate>
		</item>
		<item>
			<title>Going solo and running your own consulting business: Is it for you?</title>
			<description>Columnist explores the ups and downs of being your own boss.</description>
			<link>http://www.uberasp.net/GetArticle.aspx?id=14</link>
			<pubDate>Mon, 12 Jan 2004 03:48:35 GMT</pubDate>
		</item>
		<item>
			<title>Use an HttpHandler to stop bandwidth leeching of your images</title>
			<description>HttpHandlers make it easy to handle certain file types with a simple interface that anyone can write 
			code for.</description>
			<link>http://www.uberasp.net/GetArticle.aspx?id=13</link>
			<pubDate>Fri, 09 Jan 2004 06:14:21 GMT</pubDate>
		</item>
	</channel>
</rss>

Pretty exciting, eh? There's a lot to learn about XML, but frankly you don't need to learn much to understand what's going on here. The document starts with the required XML declaration, and from there a series of tags define the data with plain everyday words you know.

Now go check out the RSS 2.0 Specification, and you can see why we have included the elements in this document. The big picture "channel" requires only three elements: title, link and description. We've added description and ttl ("time to live," which tells aggregators how long before the data is "old") because, well, because we want to.

The next important part of the spec is what you put in each item. The only thing required here is the title or description. We're using both, plus a link back to the article (because we want people to read the entire story, duh), and the pubDate element so the end-user can see how new the headline is.

The spec gets pretty detailed about what you can add and how to do it. Because XML is supposed to be extensible, you aren't limited to just the specified elements. You can add others as well, provided you specify a namespace and the overall document meets the specification. XML namespaces aren't complicated, but in the interest of keeping things simple, we won't go into it here.

Writing an XML document the .Net way

If you've been using the .Net Framework for even a week, you know that the kids in Redmond really thought of almost everything, so they're not going to make you concatenate huge strings to build an XML document. For our example, we're going to create a new page on our site called NewsWire.aspx and use that as the URL for our RSS feed. We're going to make the .aspx file contain only two lnes:

<%@ Page Codebehind="NewsWire.aspx.cs" Inherits="UberAspNet.RSS.NewsWire" EnableViewState="false" %>
<%@ OutputCache Duration="300" VaryByParam="none" %>

The first line you've seen before. The second line tells ASP.NET to cache the entire page in memory and not run the code until 300 seconds have passed. You can put whatever value you want here, but if the data you're going to display changes infrequently, there's no harm really in cranking it up. Cached output is a lot faster than executing code and hitting a database. The rest of the .aspx file contains nothing. No HTML, XML or links to nudies.

Frankly, the code-behind isn't that complicated either. Here are the goods:

C#

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Web;
using System.Xml;

namespace UberAspNet.RSS
{
	public class NewsWire : System.Web.UI.Page
	{
		private void Page_Load(object sender, System.EventArgs e)
		{
			Response.Clear();
			Response.ContentType = "text/xml";
			XmlTextWriter objX = new XmlTextWriter(Response.OutputStream, Encoding.UTF8);
			objX.WriteStartDocument();
			objX.WriteStartElement("rss");
			objX.WriteAttributeString("version","2.0");
			objX.WriteStartElement("channel");
			objX.WriteElementString("title", "uberASP.Net NewsWire");
			objX.WriteElementString("link","http://www.uberasp.net/newswire.aspx");
			objX.WriteElementString("description","The latest headlines and articles from the world of ASP.NET, Microsoft's 
															Web development platform.");
			objX.WriteElementString("copyright","(c) 2004, POP World Media, LLC. All rights reserved.");
			objX.WriteElementString("ttl","5");
			SqlConnection objConnection = new SqlConnection(ConfigurationSettings.AppSettings["MyConnectionString"]);
			objConnection.Open();
			string sql = "SELECT TOP 10 Title, Summary, ArticleID, PostTime FROM Articles ORDER BY PostTime DESC";
			SqlCommand objCommand = new SqlCommand(sql, objConnection);
			SqlDataReader objReader = objCommand.ExecuteReader();
			while (objReader.Read())
			{
				objX.WriteStartElement("item");
				objX.WriteElementString("title",objReader.GetString(0));
				objX.WriteElementString("description",objReader.GetString(1));
				objX.WriteElementString("link","http://www.uberasp.net/GetArticle.aspx?id=" + objReader.GetInt32(2).ToString());
				objX.WriteElementString("pubDate", objReader.GetDateTime(3).ToString("R"));
				objX.WriteEndElement();
			}
			objReader.Close();
			objConnection.Close();

			objX.WriteEndElement();
			objX.WriteEndElement();
			objX.WriteEndDocument();
			objX.Flush();
			objX.Close();
			Response.End();
		}
	}
}

VB .Net

Imports System
Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient
Imports System.Text
Imports System.Web
Imports System.Xml


Namespace UberAspNet.RSS

   Public Class NewsWire
      Inherits System.Web.UI.Page
      
      Private Sub Page_Load(sender As Object, e As System.EventArgs)
         Response.Clear()
         Response.ContentType = "text/xml"
         Dim objX As New XmlTextWriter(Response.OutputStream, Encoding.UTF8)
         objX.WriteStartDocument()
         objX.WriteStartElement("rss")
         objX.WriteAttributeString("version", "2.0")
         objX.WriteStartElement("channel")
         objX.WriteElementString("title", "uberASP.Net NewsWire")
         objX.WriteElementString("link", "http://www.uberasp.net/newswire.aspx")
         objX.WriteElementString("description", "The latest headlines and articles from the world of ASP.NET, 
																	Microsoft's Web development platform.")
         objX.WriteElementString("copyright", "(c) 2004, POP World Media, LLC. All rights reserved.")
         objX.WriteElementString("ttl", "5")
         Dim objConnection As New SqlConnection(ConfigurationSettings.AppSettings("MyConnectionString"))
         objConnection.Open()
         Dim sql As String = "SELECT TOP 10 Title, Summary, ArticleID, PostTime FROM Articles ORDER BY PostTime DESC"
         Dim objCommand As New SqlCommand(sql, objConnection)
         Dim objReader As SqlDataReader = objCommand.ExecuteReader()
         While objReader.Read()
            objX.WriteStartElement("item")
            objX.WriteElementString("title", objReader.GetString(0))
            objX.WriteElementString("description", objReader.GetString(1))
            objX.WriteElementString("link", "http://www.uberasp.net/GetArticle.aspx?id=" + objReader.GetInt32(2).ToString())
            objX.WriteElementString("pubDate", objReader.GetDateTime(3).ToString("R"))
            objX.WriteEndElement()
         End While
         objReader.Close()
         objConnection.Close()
         
         objX.WriteEndElement()
         objX.WriteEndElement()
         objX.WriteEndDocument()
         objX.Flush()
         objX.Close()
         Response.End()
      End Sub
   End Class
End Namespace

First we show which namespaces we'll need. System.Text is necessary because we're accessing the Encoding class. Naturally since we're creating XML, we also need System.Xml.

Once we get to the actual Page_Load processing, we clear the Response object and set its ContentType to "text/xml." Then we create an XmlTextWriter object that will do all of the heavy lifting. In our case, we're outputting it to Response.OutputStream, but you could just as easily output to a FileStream object and save it as a file.

The rest of the code reads a lot like the output that it will create. We must start the document with the XmlTextWriter's WriteStartDocument() method and end with the WriteEndDocument() method. The first line that declares this as an XML document is created for us.  If we're going to create an element that doesn't have any child nodes, we simply use the WriteElementString() method. If the element will have child nodes, we have to start it with the WriteStartElement() method and end it with the WriteEndElement().

The only thing you really need to watch for here is the fact that, per the specification, we need to add an attribute to the opening rss element that indicates what version of RSS we're using. As soon as we've called WriteStartElement(), we call the WriteAttributeString() method to insert what will output as version="2.0."

The data access code should look familiar enough. We'll get a DataReader from the database and keep calling its Read() method and create item elements filled with our title, description, link and pubDate. Notice that for the pubDate we specify a format string for the date, in this case, "R" for the long RFC 822 format required by the spec. This time should be UTC (formerly known as GMT), but that will depend on how you're storing it.

Once we've written all of our tags out, we'll call the Flush() and Close() methods of the XmlTextWriter and then call the Response object's End() method to send it all of to the client. That's it!

We can for the most part trust that the XmlTextWriter is doing things right in terms of creating well-formed XML, but just to make sure you're not violating the specification, there are a number of validators to check your work. You should also feed your finished feed into some popular RSS readers/content aggregators, just to make sure. Some of them don't follow the spec as well as they should though, so while you might have a valid feed, you won't always have a valid consumer.

It's also way cool and trendy to mark your feed with a little RSS Feed icon, so feel free to save it and use it in your own project.