Geeks With Blogs
Łukasz Kuryło's blog

Create a rss or atom feed is very simple in asp.net mvc. In V1.0 there isn’t a build-in mechanism to work with feeds, however we can very quickly build our own. All we have to do is create a xml structure accordance with their specification and new ActionResult derived class to handle the result.  But let’s start form beginning.

 

The first of all, we need to create a new MVC project and call it e.g. RssFeed. The data for feeds will be taken from database, so we have to build model. Let’s create a new database called SampleDB with one table Article, like shown below and add some records for testing.

 

 









Next step is to create a DataContext class. Select the Model catalog from Soluction Explorer,  add a new item called LINQ to SQL Classes with name SampleDB.dbml and drag from Server Explorer  the Article table to it.

 

 








The data taken from database we need to handle somewhere, so we can create a two classes. One for channel data, like title or description, and second one for information described ours articles:

 

    public class Feed

    {  

        //channel informations

        public string Title { set; get; }

        public string Description { set; get; }

        public string Link { set; get; }

        public string Author { set; get; }

        public List<FeedItem> Items { set; get; }

        public DateTime Updated { set; get; }

    }

 

    public class FeedItem

    {  

        //feed informations

        public string Title { set; get; }

        public string Description { set; get; }

        public DateTime PublicDate { set; get; }

        public Guid Id { set; get; }

    }

 

We will fill these classes in the controller, so add a new controller called FeedController. In this class we create a action method(s) to handle the RSS and/or Atom feeds with the data provided in the ActionResult constructor.

 

    public class FeedController : Controller

    {

        SampleDBDataContext db = new SampleDBDataContext();

 

        [NonAction]

        private Feed GetData()

        {

            Feed rss = new Feed()

            {

                Description = "Site description",

                Link = "link",

                Title = "Simple rss/atom feed",

                Author = "Author name",

                Updated = DateTime.Now

            };

 

            //get articles

            var articles = from a in db.Articles

                           select new FeedItem

                           {

                               PublicDate = a.PublicDate,

                               Title = a.Title,

                               Description = a.Content,

                               Id = a.ArticleId                            

                           };

 

            rss.Items = articles.ToList();

 

            return rss;

        }

 

        public ActionResult RSS()

        {

            Feed rss = GetData();

 

            return new RssResult(rss);

        }

 

        public ActionResult Atom()

        {

            Feed atom = GetData();

 

            return new AtomResult(atom);

        }

    }

 

 

At the end we need to create a new ActionResult derived classes. One for Atom and one for RSS.  In these classes we will use a LINQ to XML to build a xml file structure with the data provided in constructor and send the result to the user in ExecuteResult method.

 

RSS feed

 

    public class RssResult : ActionResult

    {

        public Feed RssFeeds

        {

            set;

            get;

        }

 

        public RssResult(Feed f)

        {

            RssFeeds = f;

        }

 

        private XDocument CreateXmlDoc()

        {

            //create the rss xml structure

            XDocument doc = new XDocument(

                new XDeclaration("1.0", "UTF-8", ""),

                new XElement("rss",

                    new XAttribute("version", "2.0"),

                    new XElement("channel",

                        new XElement("title", RssFeeds.Title),

                        new XElement("link", RssFeeds.Link),

                        new XElement("description", RssFeeds.Description)

                        )));

 

            foreach (FeedItem item in RssFeeds.Items)

            {

                XElement i = new XElement("item",

                      new XElement("title", HttpUtility.HtmlEncode(item.Title)),

                      new XElement("link""http://localhost:3563/Article/" + item.Id),

                      new XElement("pubDate", item.PublicDate),

                      new XElement("description", HttpUtility.HtmlEncode(item.Description)));

 

                doc.Element("rss")

                    .Element("channel")

                    .Add(i);

            }

 

            return doc;

        }

 

        public override void ExecuteResult(ControllerContext context)

        {

            context.HttpContext.Response.ContentType = "application/rss+xml";

 

            using (System.Xml.XmlWriter writer =

                System.Xml.XmlWriter.Create(context.HttpContext.Response.Output))

            {

                CreateXmlDoc().Save(writer);

            }

 

        }

    }

 

Atom feed


    public class AtomResult : ActionResult

    {

        public Feed AtomFeeds

        {

            set;

            get;

        }

 

        public AtomResult(Feed f)

        {

            AtomFeeds = f;

        }

 

        private XDocument CreateXmlDoc()

        {

 

            XNamespace ns = "http://www.w3.org/2005/Atom";

 

            XDocument doc = new XDocument(

                new XDeclaration("1.0", "UTF-8", ""),

                new XElement(ns + "feed",

                    new XElement(ns + "title", AtomFeeds.Title),

                    new XElement(ns + "link",

                        new XAttribute("href", "http://localhost:3563/Feed/Atom"),

                        new XAttribute("rel", "self")),

                    new XElement(ns + "updated",

                    //updated element must be in

                    //year-month-dayThour:minuts:secondsTimeZone format

                        AtomFeeds.Updated.ToString("yyyy-MM-dd\\THH:mm:ss%K")),

                    new XElement(ns + "author",

                        new XElement(ns + "name", AtomFeeds.Author)),

                    //id must be constant and unique for this channel

                    //if uri address is used, it hasn't be real

                    new XElement(ns + "id", "http://localhost:3563/MyAtomFeedId")

                    ));

 

            foreach (FeedItem item in AtomFeeds.Items)

            {

                XElement i = new XElement(ns + "entry",

                      new XElement(ns + "title", item.Title),

                      new XElement(ns + "link",

                          new XAttribute("href", "/Article/" + item.Id)),

                      //id must be constant and unique, otherwise each update

            //by feed readers will be duplicating all entries

                      new XElement(ns + "id", item.Id),

                      new XElement(ns + "updated",

                          item.PublicDate.ToString("yyyy-MM-dd\\THH:mm:ss%K")),

                      new XElement(ns + "summary",

                          item.Description.Length > 255 ?

                                item.Description.Substring(0, 254).Insert(254, "...")

                                    : item.Description));

 

                doc.Element(ns + "feed")

                    .Add(i);

            }

 

            return doc;

        }

 

        public override void ExecuteResult(ControllerContext context)

        {

            context.HttpContext.Response.ContentType = "application/atom+xml";

 

            using (System.Xml.XmlWriter writer =

                System.Xml.XmlWriter.Create(context.HttpContext.Response.Output))

            {

                CreateXmlDoc().Save(writer);

            }

        }

    }

 

At the really end we can add a link(s) for the channel(s) in the master page. Thanks to them, in the address bar e.g. in Firefox we will have a small icon which we can add subscribe to our channel.

Link for RSS:


<link href="<%= Url.Content("/Feed/RSS") %>" type="application/rss+xml" rel="alternate" title="RSS Feed" />


Link for Atom:


<link href="<%= Url.Content("/Feed/Atom") %>" type="application/atom+xml" rel="alternate" title="Atom Feed" />


Full source code for this example can be found here (click the blue button called "Pobierz plik").

 

Implementing a new ActionResult classes is only one of the ways to build this stuff. Other is using the standard View engine. It is possible to derive new class from ViewPage which is strongly-typed with model.

Beyond generation the xml file structure manually in our ActionResult classes, there is also possibility to use a build-in in .NET Framework classes: Atom10FeedFormatter and Rss20FeedFormatter. This keeps us from knowing the structure of xml files for each channel.

 

Examples for that are available here:

 

- Guy Burstein's blog post (strongly-typed ViewPage) 

- Rss20FeedFormatter class

- Atom10FeedRormatter class


A RSS/Atom documents structures are described here


http://cyber.law.harvard.edu/rss/rss.html

http://www.ietf.org/rfc/rfc4287.txt

Posted on Sunday, August 23, 2009 8:23 PM ASP.NET MVC , Linq | Back to top


Comments on this post: Own RSS/Atom feed in ASP.NET MVC

# re: Own RSS/Atom feed in ASP.NET MVC
Requesting Gravatar...
Did the same thing, but I used a normal view (aspx page) to render the RSS XML.
Left by Will on Aug 25, 2009 9:42 PM

# re: Own RSS/Atom feed in ASP.NET MVC
Requesting Gravatar...
Thank you. I apply the atom feed to my webpage but in local it shows nothing to me. I don't know why. Actually when I debug and look to xml output it shows everything.
Left by kad1r on Oct 30, 2010 11:17 AM

Comments have been closed on this topic.
Copyright © Łukasz Kuryło | Powered by: GeeksWithBlogs.net