Using ASP.Net 3.5 to read an RSS feed asynchronously
Among the new features introduced in .Net 3.5 is the System.ServiceModel.Syndication namespace, which containes classes allowing the reading and writing of syndication feeds in RSS 2.0 and Atom 1.0 format. The idea is to abstract the individual XML structures of these feeds through formatters, in this case the Atom10FeedFormatter and Rss20FeedFormatter.
Recently I needed to pull an RSS feed asynchronously via javascript and display the contents in an expandable list. The basic plan was to use a proxy service (a HTTP handler) which would go and get the RSS (or Atom) feed and pull out a user configurable number of recent posts.

Normally this would have required reading the XML feed and manually parsing the elements via an XML reader or XMLDocument. However, the new features in System.ServiceModel.Syndication made this a snap.
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Web;
using System.ServiceModel.Syndication;
namespace darrenjohnstone.net.RSSLibrary
{
/// <summary>
/// The SyndicationService handler is used to retrieve RSS feeds from external services
/// and supply them to the calling scripts. This proxy is used to avoid security issues with
/// cross-domain client side HTTP requests.
/// </summary>
public class SyndicationService : IHttpHandler
{
const string ERROR_LINE = "<error>{0}</error>";
/// <summary>
/// Constructor.
/// </summary>
public SyndicationService()
{
}
/// <summary>
/// Gets an RSS feed from a given URL and returns the resulting XML as a string.
/// </summary>
/// <param name="url">URL of the feed to load.</param>
/// <param name="count">The count of postings to return.</param>
/// <returns>The feed XML as a string.</returns>
public string GetFeed(string url, int count)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
settings.CheckCharacters = true;
settings.CloseInput = true;
settings.IgnoreComments = true;
settings.IgnoreProcessingInstructions = true;
settings.ProhibitDtd = false;
try
{
using (XmlReader reader = XmlReader.Create(url, settings))
{
SyndicationFeedFormatter formatter = null;
Atom10FeedFormatter atom = new Atom10FeedFormatter();
Rss20FeedFormatter rss = new Rss20FeedFormatter();
SyndicationFeed feed;
// Determine the format of the feed
if (reader.ReadState == ReadState.Initial)
{
reader.MoveToContent();
}
if (atom.CanRead(reader))
{
formatter = atom;
}
if (rss.CanRead(reader))
{
formatter = rss;
}
if (formatter == null)
{
return String.Format(ERROR_LINE, "The feed format is not supported");
}
formatter.ReadFrom(reader);
feed = formatter.Feed;
// Remove unwanted items
List<SyndicationItem> items = new List<SyndicationItem>();
int added = 0;
foreach (SyndicationItem i in feed.Items)
{
items.Add(i);
if (added++ == count - 1) break;
}
feed.Items = items;
StringWriter sw = new StringWriter();
XmlTextWriter tw = new XmlTextWriter(sw);
feed.SaveAsRss20(tw);
return sw.ToString();
}
}
catch
{
return String.Format(ERROR_LINE, "The feed could not be read. Please check the URL.");
}
}
/// <summary>
/// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the
/// intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
public void ProcessRequest(HttpContext context)
{
int count = 0;
string xml;
context.Response.ContentType = "text/html";
int.TryParse(context.Request["count"], out count);
xml = GetFeed(context.Server.UrlDecode(context.Request["url"]), count);
context.Response.Write(XMLUtilities.Transform(xml, context.Server.MapPath("~/RSS_20_HTML.xslt")));
}
/// <summary>
/// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance.
/// </summary>
/// <value></value>
/// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.</returns>
public bool IsReusable
{
get
{
return false;
}
}
}
}
An XMLReader is used to load the content of the feed from an external URL. Once this has been done, the format of the feed is determined by using the CanRead methods of the two formatters in turn. Everything except the first few posts are then discarded. The next trick is to use the SaveAsRss20 method of the formatter to save the newly trimmed feed as an RSS 2.0 formatted XML string. Why? Because I wanted to use an XSL transform to create the output HTML and didn’t want it to have to deal with Atom feeds separately- so everything is saved in RSS format.
So we now have an HTTP handler which accepts a url and post count as URL parameters and returns HTML for presentation on the client. This allows us to use XMLHttpRequest (or Microsoft.XMLHTTP on Internet Explorer) to make an asynchronous call from the clent to get the feed contents after the main page has loaded. Since we are using a proxy service to get the external RSS feeds we avoid the cross domain scripting problems that we would otherwise get.
The feed reader has been wrapped into an ASP.Net server control which displays a spinner graphic whilst loading the RSS content using javascript and an XML HTTP request. Once this is complete the content of the control is replaced with the RSS feed HTML as returned from the service.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace darrenjohnstone.net.RSSLibrary
{
/// <summary>
/// Implements a single RSS feed gadget. This control uses an XHTTP request
/// to load a transformed RSS feed from the proxy handler.
/// </summary>
public class RSSGadget : WebControl
{
const string JS_PATH = "RSSFeed.js";
const string PROXY_URL = "SyndicationService.ashx";
const string CSS_PATH = "styles/RSS.css";
string _containerId;
/// <summary>
/// Gets or sets the URL.
/// </summary>
/// <value>The URL.</value>
public string Url
{
get;
set;
}
/// <summary>
/// Gets or sets the count of items to return.
/// </summary>
/// <value>The count of items to return.</value>
public int Count
{
get;
set;
}
/// <summary>
/// Initializes a new instance of the <see cref="RSSGadget"/> class.
/// </summary>
public RSSGadget()
{
}
/// <summary>
/// Raises the <see cref="E:System.Web.UI.Control.Load"/> event.
/// </summary>
/// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_containerId = this.UniqueID + "_container";
if (!Page.ClientScript.IsClientScriptIncludeRegistered(this.GetType(), "RSSFeed_js"))
{
Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "RSSFeed_js", JS_PATH);
HtmlLink link = new HtmlLink();
link.Attributes.Add("type", "text/css");
link.Attributes.Add("rel", "stylesheet");
link.Attributes.Add("href", CSS_PATH);
Page.Header.Controls.Add(link);
}
}
/// <summary>
/// Renders the control to the specified HTML writer.
/// </summary>
/// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.</param>
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
var req_id = "req_" + UniqueID;
writer.Write("<div id='");
writer.Write(_containerId);
writer.Write("' class='RSS_Gadget'>");
writer.Write("<div class='RSS_Header' />");
writer.Write("Loading...");
writer.Write("</div>");
writer.Write("<div class='RSS_Wait'/>");
writer.Write("</div>");
writer.Write("</div>");
writer.Write("<script language='javascript' type='text/javascript'>");
writer.Write("var " + req_id + "; ");
writer.Write("rss_addLoadEvent(function(){");
writer.Write(req_id + "=rss_loadXMLDoc('");
writer.Write(PROXY_URL);
writer.Write("?id=" + Guid.NewGuid().ToString());
writer.Write("&count=" + Count.ToString());
writer.Write("&url=" + Page.Server.UrlEncode(Url));
writer.Write("', function() {");
writer.Write("rss_callback('");
writer.Write(_containerId);
writer.Write("'");
writer.Write(", " + req_id);
writer.Write(");");
writer.Write("});});");
writer.Write("</script>");
}
}
}
The control can then be referenced and used by setting the Url and Count parameters:
<cc1:RSSGadget ID="RSSGadget1" runat="server" Url="http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/scotland/rss.xml" Count="6" />
A demo Visual Studio 2008 project can be found in the downloads section.

Comment by Carol on 12 July 2008:
I am not a coder. All I want is very simple: put an RSS feed on my website hosted by server with asp only, no php. Don’t want to use JS because search engines don’t read it.
Now what? I’ve spent hours searching; no luck so far.
Comment by darren on 12 July 2008:
Hi Carol,
The control is pretty simple to change to just output the RSS feed when loaded instead of using javascript.
To make things simple I’ve repacked the control for you. This version is a user control which doesn’t use any javascript for getting the feed. Script is used to expand the tree nodes but this is optional and is search engine friendly anyway.
Just change the CSS file to suit. A sample project is provided.
Get the download here http://darrenjohnstone.net/download/10/.
Hope this helps.
Cheers,
Darren
Comment by Bill on 21 August 2008:
So I set down all ready to parse my rss mxl. Then I came across your blog post. Thanks for the great introduction. I did not even know abou the syndication classes.
Comment by Chris on 3 September 2008:
This seems to be exactly what I am looking for. However, my knowledge of coding is basic enough that I don’t know how to implement the function. Would you be willing to tell me what I need to do to include an RSS Feed within an existing website layout via the non-javascript control?
Cheers…
Comment by darren on 4 September 2008:
Hi Chris,
The sample project provides everything you need for this.
On the page where you want to display the feed add the following declaration to include the control. Put this at the top of the page under the
pagedeclaration.< %@ Register src="RSSFeed.ascx" tagname="RSSFeed" tagprefix="uc1" %>
Next include the control at the point in the page where you want it displayed:
<uc1:RSSFeed ID="RSSFeed" runat="server" Url="http://darrenjohnstone.net/feed" Count="6" ></uc1:RSSFeed>
Set Url to the url of the feed and count to a positive integer value which will determine the number of posts to display.
That’s it. Remember that the control only works in version 3.5 of ASP.Net.
Any problems give me a shout.
Cheers,
Darren
Comment by TDubb on 29 September 2008:
I am currently using your control, but would like to put it within an update panel. It seems to hang when I do this. Is there anything special I need to do to make it work within an updatepanel? I’m not showing the rssgadget until a button click event. The panel shows the control, however, the control never gets out of the loading phase. I have ensured that the same control works outside of the panel, it just seems to be in a never-ending loop..
Thanks for your help.
Comment by darren on 30 September 2008:
TDubb-
The control makes asynchronous requests to get the RSS feed data. I’ve not tried this within an update panel but I don’t quite understand what you are trying to do since the control is already asynchronous. Can you let me know what you’re trying to achieve and I’ll see if I can help.
If you simply want to hide the control until a button click then this would be best handled on client side with javascript.
Cheers
Darren
Comment by Mithun Bagdai on 14 October 2008:
Hi, this is a wonderful demo and works fantastically. I just want to know how would I customize the look of this mini rss reader so that I can add lets say images or have this in more widget type looking box.
Thank you
Comment by Inflecto Systems (Software Developpers) on 13 November 2008:
Nice article. I didn’t know this namespace existed in the framework but it’s just what I am after for a small internal project I am working on.
Comment by Tom on 16 January 2009:
Nice piece of coding, Darren. Clean, well-thought out and documented. Wow.
Comment by Artem on 29 January 2009:
I tried this control. Very cool. The problem is that this rss reader
(as well as others) works only in debug mode in my case. Working site gives error - “The feed could not be read. Please check the URL” in this case or “connection forcebly closed” or “connection timeout” in otners. I test all the controls on localhost. Something with net configuration probably.
Comment by Howard on 15 February 2009:
Hi,
Very impressed with this. However my website is being developed in VB and not C#. I have run it through a VB converter. The only error is in the ’static string Transform(string xml, string xsl)’ function where it reports a variable cannot have the same name as the function. I tried renaming the transform variable to transform1 and although the application runs the RSS feed doesn’t display on the page. I have added the original RSSFeed web control written in C# to my project and it seems to run OK. I am relatively new to web developing, is this permissible or is it possible to get a VB version of the control?
Many thanks
Comment by darren on 15 February 2009:
Hi Howard,
There’s no reason I can think of why you can’t convert the control to VB.Net. If you send me the conversion I’ll take a quick look and see what’s wrong.
Cheers
Comment by Howard on 17 February 2009:
Hi,
Have sent the files to your email address.
Thanks,
Howard.