Creating RSS feeds in ASP.NET MVC

ASP.NET MVC is the technology that brought me to Microsoft and the west-coast and it’s been fun getting to grips with it these last few weeks.

Last week I needed to expose RSS feeds and checked out some examples online but was very disappointed.

If you find yourself contemplating writing code to solve technical problems rather than the specific business domain you work in you owe it to your employer and fellow developers to see what exists before churning out code to solve it.

The primary excuse (and I admit to using it myself) is “X is too bloated, I only need a subset. I can write that quicker than learn their solution.” but a quick reality check:

  • Time – code always takes longer than you think
  • Bloat – indicates the problem is more complex than you realize
  • Growth – todays requirements will grow tomorrow
  • Maintenance – fixing code outside your business domain
  • Isolation – nobody coming in will know your home-grown solution

The RSS examples I found had their own ‘feed’ and ‘items’ classes and implemented flaky XML rendering by themselves or as MVC view pages.

If these people had spent a little time doing some research they would have discovered .NET’s built in SyndicatedFeed and SyndicatedItem class for content and two classes (Rss20FeedFormatter and Atom10FeedFormatter )  to handle XML generation with correct encoding, formatting and optional fields.

All that is actually required is a small class to wire up these built-in classes to MVC.

using System;
using System.ServiceModel.Syndication;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Xml;

namespace MyApplication.Something
{
    public class FeedResult : ActionResult
    {
        public Encoding ContentEncoding { get; set; }
        public string ContentType { get; set; }

        private readonly SyndicationFeedFormatter feed;
        public SyndicationFeedFormatter Feed{
            get { return feed; }
        }

        public FeedResult(SyndicationFeedFormatter feed) {
            this.feed = feed;
        }

        public override void ExecuteResult(ControllerContext context) {
            if (context == null)
                throw new ArgumentNullException("context");

            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/rss+xml";

            if (ContentEncoding != null)
                response.ContentEncoding = ContentEncoding;

            if (feed != null)
                using (var xmlWriter = new XmlTextWriter(response.Output)) {
                    xmlWriter.Formatting = Formatting.Indented;
                    feed.WriteTo(xmlWriter);
                }
        }
    }
}

In a controller that supplies RSS feed simply project your data onto SyndicationItems and create a SyndicationFeed then return a FeedResult with the FeedFormatter of your choice.

public ActionResult NewPosts() {
    var blog = data.Blogs.SingleOrDefault();
    var postItems = data.Posts.Where(p => p.Blog = blog).OrderBy(p => p.PublishedDate).Take(25)
        .Select(p => new SyndicationItem(p.Title, p.Content, new Uri(p.Url)));

    var feed = new SyndicationFeed(blog.Title, blog.Description, new Uri(blog.Url) , postItems) {
        Copyright = blog.Copyright,
        Language = "en-US"
    };

    return new FeedResult(new Rss20FeedFormatter(feed));
}

This also has a few additional advantages:

  1. Unit tests can ensure the ActionResult is a FeedResult
  2. Unit tests can examine the Feed property to examine results without parsing XML
  3. Switching to Atom format involved just changing the new Rss20FeedFormatter to Atom10FeedFormatter

[)amien

10 responses  

  1. That code makes me happy. I can’t define good code, but I know it when I see it. Simple, extensible, testable, re-uses pre existing artwork that’s baked into the framework, it ticks a whole lot of boxes. Nice work Mr Guard.

    Dan F – April 27th, 2010
  2. pingback

    [...] Creating RSS feeds in ASP.NET MVC (Damien Guard) [...]

    Dew Drop – April 27, 2010 | Alvin Ashcraft's Morning DewApril 27th, 2010
  3. Hi Damien,
    Thanks for the well written article, RSS is really the glue that holds a lot of services together, leveraging it is well worth while.

    On a side note, I was very happy to see your code using the “Envy Code R” font, I’ve switched to that recently and haven’t looked back :)

    -Jarrod

    Jarrod N – April 28th, 2010
  4. I’ve been using a similar method for producing feeds for some time.
    I’ve found that you suffer if you start requiring CDATA.

    While my solution isn’t quite ideal, this is the technique I use to get around the lack of CDATA support in the .NET SyndicationFeed objects.

    public class RssResult : ActionResult {
            public SyndicationFeed Feed { get; set; }
    
            public RssResult(SyndicationFeed feed) {
                Feed = feed;
            }
    
            public override void ExecuteResult(ControllerContext context) {
                context.HttpContext.Response.ContentType = "application/xml";
                var rssFormatter = new Rss20FeedFormatter(Feed);
                var settings = new XmlWriterSettings {
                                                 NewLineHandling = NewLineHandling.None,
                                                 Indent = true,
                                                 Encoding = Encoding.UTF32,
                                                 ConformanceLevel = ConformanceLevel.Document,
                                                 OmitXmlDeclaration = true
                                             };
    
                var buffer = new StringBuilder();
                var cachedOutput = new StringWriter(buffer);
    
                using (var writer = XmlWriter.Create(cachedOutput, settings)) {
                    if (writer != null) {
                        rssFormatter.WriteTo(writer);
                        writer.Close();
                    }
                }
                
                var xmlDoc =  XDocument.Parse(buffer.ToString());
                foreach (var element in xmlDoc.Descendants("channel").First().Descendants("item").Descendants("description"))
                    VerifyCdataHtmlEncoding(buffer, element);
    
                foreach (var element in xmlDoc.Descendants("channel").First().Descendants("description"))
                    VerifyCdataHtmlEncoding(buffer, element);
    
                buffer.Replace(" xmlns:a10=\"http://www.w3.org/2005/Atom\"", " xmlns:atom=\"http://www.w3.org/2005/Atom\"");
                buffer.Replace("a10:", "atom:");
    
                context.HttpContext.Response.Output.Write(buffer.ToString());
            }
    
            private static void VerifyCdataHtmlEncoding(StringBuilder buffer, XElement element) {
                if(element.Value.Contains("")) {
                    string cdataValue = string.Format("", element.Name, element.Value, element.Name);
                    buffer.Replace(element.ToString(), cdataValue);
                }
            }
        }
    David WhitneyApril 29th, 2010
  5. pingback

    [...] Guard – Creating RSS feeds in ASP.NET MVC,via [...]

    TWC9: Windows Phone 7 Dev Tools Refresh, Messenger Wave 4 Preview | CHARGED's Digital Lifestyle at Work or PlayMay 9th, 2010
  6. pingback

    [...] Creating RSS Feeds in ASP.NET MVC: Damien Guard has a nice post that describes a cool new “FeedResult” class he created that makes it easy to publish and expose RSS feeds from within ASP.NET MVC sites. [...]

    May 20th Links: ASP.NET MVC, ASP.NET, .NET 4, VS 2010, Silverlight - ScottGu's BlogMay 20th, 2010
  7. pingback

    [...] RSS Feed to my blog in 10 minutes Damien Guard has a nice solution on Creating RSS feeds in ASP.NET MVC. He relied on the .NET Framework built classes rather than cooking up things ourselves.  I was [...]

    Adding RSS Feed to my blog in 10 minutes « Ray’s WeblogNovember 3rd, 2010
  8. I just got a message regarding my above comment. Appears that the markup stripping in this blog has stripped out some of the code, which makes my comment appear insane!

    Here’s a gist with the snippet that might make it make sense: https://gist.github.com/1027181

    The issue we were having was that elements were getting HTMLEncoded by default, ruining their entire purpose.

    David WhitneyJune 15th, 2011
  9. Thanks! I used the RSS feed ActionResult in building MessianicChords.com. In particular, I used it to generate an RSS feed for user-uploaded content to the site. That feed is then consumed by IfThisThenThat, which publishes new RSS items to Twitter and Facebook. Pretty cool stuff.

    Thanks again for posting this RSS ActionResult, was very helpful.

    Judah Gabriel HimangoFebruary 16th, 2012
  10. pingback

    [...] We’re looking to have a URL where the updates feed resides. For this, you can create a custom MVC ActionResult that wraps up an RSS feed. [...]

    Opened web FTW! How I used opened web APIs and standards to build a beautiful HTML5 web appMarch 9th, 2012

Respond to this