Register | Login

Stacking Code

public interface IBlog { string Dump(Stream consciousness); }

Adding RSS to ASP.NET MVC

Friday, 26 November, 2010 @ 5:38 PM < Adam Boddington
Tags: ASP.NET MVC, Building Neno, RSS

This is post #3 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.

As much as I want to get the lower levels of my application figured out, it's Friday afternoon, time for a beer and something a bit easier to work on. For my multitude of readers (okay, just me and a guy at work so far) I'm going to create an RSS feed with links to each individual post. Although I'm using an XML file as my data source right now, most of this code should be reusable once I get hooked up to my domain model / services. If you want to follow along, the code as of this post is tagged 2010/11/26/adding-rss-to-asp-net-mvc.

Single Post Links

The first step is to create a link for each post so the RSS feed has something to point to. I'll start with the route since I already know what the link should look like.

routes.MapRoute(
    "BlogPostByPublishDateSlug",
    "blog/{Year}/{Month}/{Day}/{Slug}",
    new { Controller = "blog", Action = "postbypublishdateslug" },
    // Use the same restrictions used by sluggify.
    new { Year = "\\d{4}", Month = "\\d{2}", Day = "\\d{2}", Slug = "[-\\d_a-z]+" });

I'm using a blog subdirectory in my URLs, but that is just a personal preference. I'm also pointing this route to a new controller, BlogController, but there is no reason why this action can't live in HomeController if desired.

The action method looks like this...

public ActionResult PostByPublishDateSlug(int year, int month, int day, string slug)
{
    var publishDate = new DateTime(year, month, day);
    string uri = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~/content/posts.xml"));

    XElement post = XDocument.Load(uri)
        .Root
        .Elements()
        .Where(element => (DateTime)element.Element("publishDate") >= publishDate)
        .Where(element => (DateTime)element.Element("publishDate") < publishDate.AddDays(1))
        .Where(element => (string)element.Element("slug") == slug)
        .Single();

    return View(post);
}

The view itself is almost exactly the same at the home index view. There's a slight modification in the post partial view, however. I've made the title into a link.

<h3><%=Html.ActionLink((string)Model.Element("title"), "PostByPublishDateSlug", "Blog", new PublishDateSlug(Model), null)%></h3>

There are two little tricks here. One is to use a custom object for the route values rather than an anonymous object. All the route data I need is held in the model, so I'll pass it to the custom object to drag out into the right form. The second trick is to expose the month and day route values as formatted strings. In the event of a single digit month or day, passing an integer would fail the route restrictions.

public class PublishDate
{
    public PublishDate(DateTime publishDate)
    {
        Year = publishDate.Year;
        Month = publishDate.Month.ToString("00");
        Day = publishDate.Day.ToString("00");
    }

    public PublishDate(XElement element)
        : this((DateTime)element.Element("publishDate"))
    {
    }

    public int Year { get; private set; }
    public string Month { get; private set; }
    public string Day { get; private set; }
}

Creating the Feed

Creating the feed is pretty straight forward these days thanks to the System.ServiceModel.Syndication namespace. I take the last five posts and transform them into SyndicationItem objects, which are then passed into a new SyndicationFeed object. That is then written out to the response stream using an XML writer and the Rss20FeedFormatter object. And that's it.

public ActionResult Rss()
{
    string uri = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~/content/posts.xml"));

    IEnumerable<SyndicationItem> posts = XDocument.Load(uri)
        .Root
        .Elements()
        .OrderByDescending(element => (DateTime)element.Element("publishDate"))
        .Take(5)
        .Select(element => new SyndicationItem(
            (string)element.Element("title"),
            new Markdown().Transform((string)element.Element("text")),
            // Getting a full URL can be done by specifying the protocol.
            new Uri(Url.Action("PostByPublishDateSlug", "Blog", new PublishDateSlug(element), Request.Url.Scheme))));

    var feed = new SyndicationFeed(
        "Stacking Code",
        "public interface IBlog { string Dump(Stream consciousness); }",
        // Getting a full URL can be done by specifying the protocol.
        new Uri(Url.Action("Index", "Home", null, Request.Url.Scheme)),
        posts);

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

    using (XmlWriter writer = XmlWriter.Create(Response.Output))
        new Rss20FeedFormatter(feed).WriteTo(writer);

    return new EmptyResult();
}

Oh, I almost forgot the last step. Add a link to the head element of each individual page -- a master page would be better, but I don't have one yet. Time to quit though and grab another beer.

<link rel="alternate" href="<%=Url.Action("rss", "blog")%>" type="application/rss+xml" title="Stacking Code" />

There are 0 comments.


Comments

Leave a Comment

Please register or login to leave a comment.


Older
Domain Model

Newer
Syntax Highlighting with Prettify

Older
Domain Model

Newer
Syntax Highlighting with Prettify

browse with Pivot


About


Projects

Building Neno


RSS
Recent Posts

Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue


Tags

Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)


Archives


Powered by Neno, ASP.NET MVC, NHibernate, and small furry mammals. Copyright 2010 - 2011 Adam Boddington.
Version 1.0 Alpha (d9e7e4b68c07), Build Date Sunday, 30 January, 2011 @ 11:37 AM