Rss Feed Generation From a Custom RssResult

When I looked at producing RSS Feeds in ASP.NET MVC before, I used the ContentResult method to return XML. Best Practice suggests that you should create a new ActionResult if required. So this article looks at creating an RssResult, and a different way to generate the feed itself.

If you look at the previous article, and its comments, you can see that there are a number of ways to produce RSS feeds in ASP.NET MVC. Some feel that you should return a model from the controller, and format it in a View, and others are happy not to have a View at all. I'm not keen on the idea of returning XML to a View template. It's prone to validation errors as it requires you to manually construct the XML. The same could be said of the approach using LINQ to XML in my previous article. However, in WCF there is an Rss20FeedFormatter class that solves the problem.

I'm going to start with the RssResult itself. This derives from ActionResult, and the ExecuteResult() method is over-ridden. You'll notice that System.ServiceModel.Syndication appears in the usings. and this is made available by adding a reference to System.ServiceModel.Web. Feed Formatters for RSS and Atom live in System.ServiceModel.Syndication. The RssResult class has a SyndicationFeed property, which is passed to the Rss20FeedFormatter object, which has a WriteTo() method. This takes an XmlWriter, and simply produces the XML as a result.


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

namespace MikesDotnetting.Models
{
    public class RssResult : ActionResult
    {
        public SyndicationFeed Feed { private get; set; }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            context.HttpContext.Response.ContentType = "text/xml";

            var rssFormatter = new Rss20FeedFormatter(Feed);
            using (var writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                rssFormatter.WriteTo(writer);
            }
        }
    }
}

At the moment, if you generate the Feed object in a Controller, you would have to call the ActionResult like this:


return new RssResult() {Feed = feed};

Ideally, we would like the call to the ActionResult to look similar to the shorthand available to existing framework ActionResults ( return View() instead of return new ViewResult()etc) so I am going to create a BaseXmlController to help with that:


using System.ServiceModel.Syndication;
using System.Web.Mvc;
using MikesDotnetting.Models;


namespace MikesDotnetting.Controllers
{
    public class BaseXmlController : Controller
    {
        protected static RssResult Rss(SyndicationFeed feed)
        {
            return new RssResult
            {
                Feed = feed
            };
        }


    }
}

Now, as with the Framework ActionResults, there is a method called Rss() which wraps the RssResult construction. So we'll have a look at the Controller action:


[OutputCache(VaryByParam = "none", Duration = 3600)]
public RssResult RssFeed()
{
    const string url = "http://www.mikesdotnetting.com/Article/{0}/{1}";
    var posts = repository.GetRSSFeed();
    var feed = new SyndicationFeed("Mikesdotnetting News Feed",
                    "Latest additions to Mikesdotnetting",
                    new Uri("http://www.mikesdotnetting.com/rss"))
                                 {
                                     Copyright =
                                             new TextSyndicationContent("(c)" + DateTime.Now.Year +
                                             ", Mikesdotnetting. All rights reserved")
                                 };
    var items = new List<SyndicationItem>();
    foreach (var post in posts)
    {
        var item = new SyndicationItem(
        			post.Head,
                    post.Intro,
                    new Uri(String.Format(url,
                    	post.ID,
                        post.Head.ToCleanUrl())),
                    post.ID.ToString(),
                    post.CreatedDate
                    );
        items.Add(item);
    }
    feed.Items = items;
    return Rss(feed);
}

I've added caching to this action, although the hour I've given it is a bit optimistic. My RSS feed changes once a week at most on average, but at least people can see new items within an hour of them appearing when they do finally make it onto my site. But you can see some of the Syndication classes at work. The top level class is a SyndicationFeed object, which is composed of a collection of SyndicationItem objects. ToCleanUrl() is an extension method I created to format the article titles to play nicely with URLs.

This approach certainly relieves the burden of generating valid RSS feeds as the Framework takes care of that for you. The use of custom ActionResults also reveals intentions very nicely. Anyone reading the code for this method can be in no doubt what it is intended to produce.