Back to all posts

Creating an RSS Feed in ASP.NET Core 3.0

Posted on Oct 30, 2019

Posted in category:
Development
ASP.NET

RSS feeds provide an excellent mechanism for websites to publish their content for consumption by others. As part of my recent website reimplementation for this site, I found myself needing to create an RSS feed to continue to support my "subscribe" function for blog posts. I found several examples; however, they always failed validation from W3C, after much trial and error I have managed to create a working RSS Feed and thought I'd share the process.

Getting Started

Microsoft publishes a library that provides a simple method to create Syndicated feeds. We need to install the NuGet package for this library.

NuGet Package Installs
Install-Package System.ServiceModel.Syndication

Using this library we will be able to quickly define our feed without much manual effort.

Create the Action

First, we want to create the action that will respond to the request for our RSS Feed.

RSS Action Method
[ResponseCache(Duration = 1200)]
[HttpGet]
public IActionResult Rss()
{

}

The only important note here is that I am setting a ResponeCache attribute on this method. I strongly recommend this as your RSS Feed generation is often something that will be going to the database etc. and feed-readers are notorious for multiple refreshes etc. By enabling ResponseCaching for your RSS action, you can reduce the load on your server.

Create the Feed

Next, we want to define the feed itself. In this step, we provide the Title, Description, URL to the site, ID of the feed, and last updated date.

Create SyndicationFeed
 var feed = new SyndicationFeed("Title", "Description", new Url("SiteUrl"), "RSSUrl", DateTIme.Now);
feed.Copyright = new TextSyndicationContent($"{DateTime.Now.Year} Mitchel Sellers");

For your implementation, you will want to provide proper values for Title, Description, Site URL and RSS URL. This defines the feed itself, you can explore other options and settings. I've included the additional copyright addition to show further examples of how you can control the content.

Add Your Items

The next step is going to vary depending on your particular implementation. In my case, I have a _blogDataService that provides a listing of entries for my RSS Feed. We get our items, determine the URL, and then add it as a syndication item, finally adding those items to our feed.

Creation of Items
var items = new List<SyndicationItem>();
var postings = _blogDataService.ListBlogForRss();
foreach (var item in postings)
{
    var postUrl = Url.Action("Article", "Blog", new { id = item.UrlSlug }, HttpContext.Request.Scheme);
    var title = item.Title;
    var description = item.Preview;                
    items.Add(new SyndicationItem(title, description, new Uri(postUrl), item.UrlSlug, item.PostDate));
}
feed.Items = items;

Render the Response

The final step in our action is to render the SyndicationFeed to the response. This process is where I found the most issues with other examples. RSS feeds should have a content type of "application/rss+xml" and should be encoded as UTF-8 to provide the best flexibility. Many examples used a StringWriter to do this rendering, which resulted in UTF-16 mixed with UTF-8. I found it is much easier to use an explicitly configured XmlWriter and a MemoryStream to render.

Render the Feed
var settings = new XmlWriterSettings
{
    Encoding = Encoding.UTF8,
    NewLineHandling = NewLineHandling.Entitize,
    NewLineOnAttributes = true,
    Indent = true
};
using (var stream = new MemoryStream())
{
    using (var xmlWriter = XmlWriter.Create(stream, settings))
    {
        var rssFormatter = new Rss20FeedFormatter(feed, false);
        rssFormatter.WriteTo(xmlWriter);
        xmlWriter.Flush();
    }
    return File(stream.ToArray(), "application/rss+xml; charset=utf-8");
} 

As you can see here, we configure our writer, for clarity and readability I did configure the writer to add additional lines and tabs. If you have a large feed you can disable those options to reduce the file size. In the end, we return the feed as a File result.

Full Code Sample

The completed method should look similar to the following.

Complete RSS Action
[ResponseCache(Duration = 1200)]
[HttpGet]
public IActionResult Rss()
{
    var feed = new SyndicationFeed("Title", "Description", new Uri("SiteUrl"), "RSSUrl", DateTime.Now);

    feed.Copyright = new TextSyndicationContent($"{DateTime.Now.Year} Mitchel Sellers");
    var items = new List<SyndicationItem>();
    var postings = _blogDataService.ListBlogForRss();
    foreach (var item in postings)
    {
        var postUrl = Url.Action("Article", "Blog", new { id = item.UrlSlug }, HttpContext.Request.Scheme);
        var title = item.Title;
        var description = item.Preview;                
        items.Add(new SyndicationItem(title, description, new Uri(postUrl), item.UrlSlug, item.PostDate));
    }

    feed.Items = items;
    var settings = new XmlWriterSettings
    {
        Encoding = Encoding.UTF8,
        NewLineHandling = NewLineHandling.Entitize,
        NewLineOnAttributes = true,
        Indent = true
    };
    using (var stream = new MemoryStream())
    {
        using (var xmlWriter = XmlWriter.Create(stream, settings))
        {
            var rssFormatter = new Rss20FeedFormatter(feed, false);
            rssFormatter.WriteTo(xmlWriter);
            xmlWriter.Flush();
        }
        return File(stream.ToArray(), "application/rss+xml; charset=utf-8");
    }
}

I hope this made your RSS feed creation easier!