Skip to main content

How to create RSS feed or Atom feed with .Net Syndication

Overview
Every other website that routinely publishes contents provides syndication feed to its users. A syndication feed can be a static XML file that gets created automatically whenever new content is published or it can be a dynamic web page that gets the latest published items and provides the appropriate XML markup. A good example of data-sharing using XML is Web site syndication, commonly found in news sites and Web logs. Another example is Twitter provides feeds of user's most recent tweets.

With the help of syndication feed, a Web site publishes its latest content in an XML-formatted file. Because it conforms to a known XML standard, its data can be parsed by any language that knows how to work with the syndication standard. There are a number of syndication formats in use, one of the more popular ones being RSS 2.0.

About Syndication in .Net
Microsoft introduced System.ServiceModel.Syndication namespace in .Net Framework 3.5, before that there was not any built-in mechanism for creating or consuming the Syndication feeds. We’ll use SyndicationFeed class of this namespace. This class has a static Load method that parses and loads the information from a specified RSS 2.0 or Atom 1.0 syndication feed. Beside that this class contains various properties like Title, Description, Links, and Copyright etc. It exposes collection of SyndicationItem objects as Items property which makes up feeds.

With the help of System.ServiceModel.Syndication namespace, creating or consuming syndication feeds from an ASP.NET application is straight forward. In this article we’ll see how to create syndication feed for your website by creating a sample web application. We’ll create both RSS 2.0 and Atom 1.0 feed based on QueryString parameter.

Creating a Syndication Feed for Your Website

First of all create an ASP.Net Empty Web Application and give it a name as “RssFeeder”.



Add a new web page to this application and give it a name “Feed.aspx”.

Remove all the HTML of the page except @Page directive and @OutputCache directive for caching of feed items, now your aspx page should be look like:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Feed.aspx.cs" Inherits="RssFeeder.Feed" %>

<%@ OutputCache Duration="60" VaryByParam="Type" %>

The @OutputCache directive caches the content returned by the syndication feed for the specified Duration - in our case, 60 seconds. The VaryByParam attribute indicates that the output caching engine should maintain a separate cached output for each unique Type value. Our Feed.aspx page accepts an optional Type querystring parameter that indicates the syndication format used. i.e., Atom 1.0(by selecting “/Feed.aspx” or “Feed.aspx?Type=Atom”) or RSS 2.0(by selecting “/Feed.aspx?Type=RSS”).

Now add two private properties (FeedType and NoOfFeeds) in our code-behind to capture the value from QueryString parameter.

public string FeedType
{
    get
    {
        return Request.QueryString["type"];
    }
}

public int NoOfFeeds
{
    get
    {
        int number;
        if (Int32.TryParse(Request.QueryString["feedsCount"], out number))
        {
            return number;
        }
        //default return 10 items.
        return 10;
    }
}

Create one proxy class, we’ll use this class to get feed items, in this case we’ll create hard coded items but in real scenario feed contents will be populated from DB or any other source.

public class FeedItemProxy
{
    public string Title { get; set; }
    public DateTime FeedDate { get; set; }
    public string Summary { get; set; }
    public string FeedUrl { get; set; }
    public string AuthorName { get; set; }
    public string AuthorEmail { get; set; }
}

We have defined all the basic properties required for feed items. Now create one method that’ll return list of FeedItemProxy class based on the number of feed items required, if there is no value for feedsCount query string parameter, it’ll select 10 as default number of feeds (see NoOfFeeds property).

/// <summary>
/// Get Feed Items
/// </summary>
/// <returns></returns>
private List<FeedItemProxy> GetFeedItems()
{
    var feedItems = new List<FeedItemProxy>();

    //Create items for selcted number of feeds.
    var totalFeeds = NoOfFeeds;

    for (var i = 1; i <= totalFeeds; i++)
    {
        feedItems.Add(new FeedItemProxy
        {
            Title = "Test Feed " + i,
            FeedDate = DateTime.Now,
            Summary = "<b>This is summary of Test Feed " + i + "</b>",
            FeedUrl = "http://www.google.co.in",
            AuthorName = "TestAuthor" + i,
            AuthorEmail = "testauthor" + i + "@test.com"
        });
    }

    return feedItems;
}

Once our feed data is ready, we’ll create one method and name it as “GenerateFeeds”, this method will be called on Page Load event to create the syndication feeds. We’ll see all code written inside this method step by step.

First of all check if feeds requested for Atom 1.0 or RSS 2.0.

var isRssFeed = (!string.IsNullOrEmpty(FeedType)
                && FeedType.ToLower() == "rss");

The syndication feed send back a different Content-Type header depending on the selected syndication format:

// set ContentType as per selected feed type.
Response.ContentType = isRssFeed
                        ? "application/rss+xml"
                        : "application/atom+xml";

We are now ready to create the SyndicationFeed object and set its properties. Add reference to System.ServiceModel dll and add System.ServiceModel.Syndication namespace.

var feed = new SyndicationFeed
{
    Title = new TextSyndicationContent("Test Feeds"),
    Language = "en-us",
    LastUpdatedTime = DateTime.Now,
    Copyright = new TextSyndicationContent("Copyright Test Feeder 2013"),
    Description = new TextSyndicationContent("This is a Test Feed loaded from Feeder")
};

The next step is to define the syndication feed's items. Create a List of SyndicationItem objects that contains a SyndicationItem instance for each item to appear in the feed. Once this List has been created assign this to the SyndicationFeed object's Items property.

//Get proxy item list for feed.
var proxyItems = GetFeedItems();

var feedItems = new List<SyndicationItem>();
foreach (var proxyItem in proxyItems)
{
    var item = new SyndicationItem
    {
        Title = SyndicationContent.CreatePlaintextContent(proxyItem.Title),
        PublishDate = proxyItem.FeedDate,
        Summary = SyndicationContent.CreateHtmlContent(proxyItem.Summary)
    };

    item.Links.Add(SyndicationLink.CreateAlternateLink(
                                 new Uri(proxyItem.FeedUrl)));

    var authInfo = new SyndicationPerson
    {
        Name = proxyItem.AuthorName,
        Email = proxyItem.AuthorEmail
    };

    item.Authors.Add(authInfo);

    // Add the item to the feed
    feedItems.Add(item);
}

feed.Items = feedItems;

In above code we created the feed items by iterating over proxy item list and then assigned this feed item list to “Items” property.

Last step is to generate the XML markup for the syndication feed and output it to the Response stream. This is handled via an XmlWriter object and either the Atom10FeedFormatter (for Atom 1.0) or Rss20FeedFormatter (for RSS 2.0) class. And in the end close the XmlWriter.

// Return the feed's XML content as the response
var feedWriter = XmlWriter.Create(Response.OutputStream);

if (isRssFeed)
{
    // RSS 2.0
    Rss20FeedFormatter rssFormatter = new Rss20FeedFormatter(feed);
    rssFormatter.WriteTo(feedWriter);
}
else
{
    Atom10FeedFormatter atomFormatter = new Atom10FeedFormatter(feed);
    atomFormatter.WriteTo(feedWriter);
}

feedWriter.Close();

We are done with creating syndication feed and we now have a fully functional syndication feed that can return either RSS 2.0 or Atom 1.0 feed XML.

Output for RSS 2.0
Get top 5 feed XML for RSS 2.0 by requesting to URL “~/Feed.aspx?type=rss&feedsCount=5” and your XML will be look like:

<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Test Feeds</title>
    <description>This is a Test Feed loaded from Feeder</description>
    <language>en-us</language>
    <copyright>Copyright Test Feeder 2013</copyright>
    <lastBuildDate>Tue, 10 Sep 2013 19:07:59 +0530</lastBuildDate>
    <item>
      <link>http://www.google.co.in/</link>
      <author>testauthor1@test.com</author>
      <title>Test Feed 1</title>
      <description>
  &lt;b&gt;This is summary of Test Feed 1&lt;/b&gt;
      </description>
      <pubDate>Tue, 10 Sep 2013 19:07:59 +0530</pubDate>
    </item>
    <item>
      <link>http://www.google.co.in/</link>
      <author>testauthor2@test.com</author>
      <title>Test Feed 2</title>
      <description>
  &lt;b&gt;This is summary of Test Feed 2&lt;/b&gt;
      </description>
      <pubDate>Tue, 10 Sep 2013 19:07:59 +0530</pubDate>
    </item>
    <item>
      <link>http://www.google.co.in/</link>
      <author>testauthor3@test.com</author>
      <title>Test Feed 3</title>
      <description>
  &lt;b&gt;This is summary of Test Feed 3&lt;/b&gt;
      </description>
      <pubDate>Tue, 10 Sep 2013 19:07:59 +0530</pubDate>
    </item>
    <item>
      <link>http://www.google.co.in/</link>
      <author>testauthor4@test.com</author>
      <title>Test Feed 4</title>
      <description>
  &lt;b&gt;This is summary of Test Feed 4&lt;/b&gt;
      </description>
      <pubDate>Tue, 10 Sep 2013 19:07:59 +0530</pubDate>
    </item>
    <item>
      <link>http://www.google.co.in/</link>
      <author>testauthor5@test.com</author>
      <title>Test Feed 5</title>
      <description>
  &lt;b&gt;This is summary of Test Feed 5&lt;/b&gt;
      </description>
      <pubDate>Tue, 10 Sep 2013 19:07:59 +0530</pubDate>
    </item>
  </channel>
</rss>

Output for Atom 1.0
Get top 5 feed XML for Atom 1.0 by requesting to URL ~/Feed.aspx?type=atom&feedsCount=5” or “~/Feed.aspx? feedsCount=5” and your XML will be look like:

<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Test Feeds</title>
  <subtitle type="text">This is a Test Feed loaded from Feeder</subtitle>
  <id>uuid:a9e7fbfc-9451-4b62-900f-b1d2093ead19;id=34</id>
  <rights type="text">Copyright Test Feeder 2013</rights>
  <updated>2013-09-10T19:34:02+05:30</updated>
  <entry>
    <id>uuid:a9e7fbfc-9451-4b62-900f-b1d2093ead19;id=35</id>
    <title type="text">Test Feed 1</title>
    <summary type="html">&lt;b&gt;This is summary of Test Feed 1&lt;/b&gt;</summary>
    <published>2013-09-10T19:34:02+05:30</published>
    <updated>2013-09-10T14:04:02Z</updated>
    <author>
      <name>TestAuthor1</name>
      <email>testauthor1@test.com</email>
    </author>
    <link rel="alternate" href="http://www.google.co.in/" />
  </entry>
  <entry>
    <id>uuid:a9e7fbfc-9451-4b62-900f-b1d2093ead19;id=36</id>
    <title type="text">Test Feed 2</title>
    <summary type="html">&lt;b&gt;This is summary of Test Feed 2&lt;/b&gt;</summary>
    <published>2013-09-10T19:34:02+05:30</published>
    <updated>2013-09-10T14:04:02Z</updated>
    <author>
      <name>TestAuthor2</name>
      <email>testauthor2@test.com</email>
    </author>
    <link rel="alternate" href="http://www.google.co.in/" />
  </entry>
  <entry>
    <id>uuid:a9e7fbfc-9451-4b62-900f-b1d2093ead19;id=37</id>
    <title type="text">Test Feed 3</title>
    <summary type="html">&lt;b&gt;This is summary of Test Feed 3&lt;/b&gt;</summary>
    <published>2013-09-10T19:34:02+05:30</published>
    <updated>2013-09-10T14:04:02Z</updated>
    <author>
      <name>TestAuthor3</name>
      <email>testauthor3@test.com</email>
    </author>
    <link rel="alternate" href="http://www.google.co.in/" />
  </entry>
  <entry>
    <id>uuid:a9e7fbfc-9451-4b62-900f-b1d2093ead19;id=38</id>
    <title type="text">Test Feed 4</title>
    <summary type="html">&lt;b&gt;This is summary of Test Feed 4&lt;/b&gt;</summary>
    <published>2013-09-10T19:34:02+05:30</published>
    <updated>2013-09-10T14:04:02Z</updated>
    <author>
      <name>TestAuthor4</name>
      <email>testauthor4@test.com</email>
    </author>
    <link rel="alternate" href="http://www.google.co.in/" />
  </entry>
  <entry>
    <id>uuid:a9e7fbfc-9451-4b62-900f-b1d2093ead19;id=39</id>
    <title type="text">Test Feed 5</title>
    <summary type="html">&lt;b&gt;This is summary of Test Feed 5&lt;/b&gt;</summary>
    <published>2013-09-10T19:34:02+05:30</published>
    <updated>2013-09-10T14:04:02Z</updated>
    <author>
      <name>TestAuthor5</name>
      <email>testauthor5@test.com</email>
    </author>
    <link rel="alternate" href="http://www.google.co.in/" />
  </entry>
</feed>

Compare RSS 2.0 and Atom 1.0
Atom Feed is similar to RSS Feed but it differ only in tags, One main difference is RSS feeds can only have one author, on the other hand in Atom 1.0 feed there can be more than one author (Check output of both format for differences in tags).

That’s it!!! You can download the working sample of this article here.

Comments

  1. how to create dynamic syndication data from the database?

    ReplyDelete
  2. RSS 2.0 feed does not validate

    ReplyDelete

Post a Comment

Popular posts from this blog

Error 405 : ASP.NET Core Web API PUT and DELETE Methods not allowed

Recently, while working with .Net core API I came across the issue of “Error 405 — Methods not Allowed” After some research, I found out that both GET and POST requests working fine but neither PUT nor DELETE requests working. Another thing is that the PUT and DELETE request was also working fine on my local machine but failed when we host on our Azure server. When I explored the issue on the web it led me to the conclusion that WebDAVModule seems to set PUT and DELETE request methods disabled by default and due to that PUT and DELETE throw 405 errors. To make the PUT and DELETE requests work, we need to override the WebDAVModule setting in web.config file by adding the below settings under “ system.webServer ”. < system.webServer >   < modules runAllManagedModulesForAllRequests = " false " >     < remove name = " WebDAVModule " />   </ modules > </ system.webServer > There may be 2 web.config files in y...

C#: Merging Excel cells with NPOI HSSFWorkbook

In this post we’ll see how to merge the two or more cell with each other while creating the excel sheet using NPOI . Mentioned below is code to merge multiple cells, in this example we are merging first cell to fifth cell of first row (you can adjust row or cell range by passing particular parameters in CellRangeAddress). //Created new Workbook var hwb = new NPOI.HSSF.UserModel. HSSFWorkbook (); //Create worksheet with name. var sheet = hwb.CreateSheet( "new sheet" ); //Create row and cell. var row = sheet.CreateRow(0); var cell = row.CreateCell(0); ; //Set text inside cell cell.SetCellValue( "This is Merged cell" ); cell.CellStyle.WrapText = true ; //define cell range address // parameters: -> first row to last and first cell to last cell var cra = new NPOI.SS.Util. CellRangeAddress (0, 0, 0, 4); //Add merged region to sheet. sheet.AddMergedRegion(cra); Hope this solution helps you J

How to set Swagger as the default start page for API hosted on the Azure web app?

I created an Asp.Net Core 2.x Web API and configured Swagger on it, below is the code added in Configure method under Startup.cs file, for full swagger configuration, check here //Add swagger configuration app.UseSwagger(); app.UseSwaggerUI(c => {     c.SwaggerEndpoint( "../swagger/v1/swagger.json" , "Test API V1" ); }); On my local machine when I run the API it is automatically redirected to the Swagger page. However, when I hosted this API as an Azure web app it is not redirecting directly to the Swagger and to access the swagger, I had to append /swagger in the URL, for example, https://testapi.azurewebsites.net/swagger/ Solution: Set RoutePrefix to string.Empty under app.UseSwaggerUI like below: app.UseSwaggerUI(c => {     c.SwaggerEndpoint( "../swagger/v1/swagger.json" , "Test API V1" );      c.RoutePrefix = string .Empty; // Set Swagger UI at apps root }); And that’s it, now when you b...