Generate Rss and Atom Feeds using Asp.Net MVC – part 2

In the previous post Generating Rss Feeds using Asp.Net MVC – part 1, we looked into how to generate Rss Feed using an Asp.Net MVC application. In this short article, we will see how can we generate variable types of Feed by just modifying couple of lines of code.

We need to determine what Feed Format request is coming in from the customer. I have modified the route registrar in global.aspx to accept a default and a custom feed request as well.

routes.MapRoute(
                "Feed", // Route name
                "{controller}/{type}", // URL with parameters
                new { controller = "Feed", action = "Index", type = UrlParameter.Optional } // Parameter defaults
            );

We will just need to mention the Feed or a combination of Feed and Type. Similar urls extension will work

// default, which is Rss
http://codingphobia.com/Feed

// for Atom Feed
http://codingphobia.com/Feed/Atom

We will need to shift some of the ExecuteResult logic out into methods and call appropriate one depending upon request.


        //For Atom Feed request
        private static void WriteToAtomFormatter(ControllerContext context, SyndicationFeed Feed)
        {
            Atom10FeedFormatter atomFormatter = new Atom10FeedFormatter(Feed);
            using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                atomFormatter.WriteTo(writer);
            }
        }

        //For Default Rss Feed request
        private static void WriteToRssFormatter(ControllerContext context, SyndicationFeed Feed)
        {
            Rss20FeedFormatter rssFormatter = new Rss20FeedFormatter(Feed);
            using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                rssFormatter.WriteTo(writer);
            }
        }

We need to modify our ExecuteResult Method like as follows

        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = "application/rss+xml";

            // check if the request is for Atom Feed
            if (context.HttpContext.Request.Url.OriginalString.Contains("Atom"))
                WriteToAtomFormatter(context, Feed);
            else
                WriteToRssFormatter(context, Feed);
        }

And this is all the work. Just with 7 lines of code and bit of modification, we may now generate Atom or Rss feed as per client request. We can also add logic for any Feed Format. Only important thing to work over is the returning ActionResult.

Generating Rss Feeds using Asp.Net MVC – part 1

This is a two part series in which I would explain a bit how can we generate Syndication feed using Asp.Net MVC and Syndication framework. first part will look into how to implement basic structure and second part will explain about how can we use a single line check and generate any type of feed, like Atom, Rss or another as per requirement.

I will start with a custom ActionResult named as FeedActionResult

public class FeedActionResult : ActionResult
{
  public SyndicationFeed Feed { get; set; }
}

We need our FeedActionResult to return our feed as an Object of Type ActionResult. Here is how we do this.

        public ActionResult GetFeed()
        {
            SyndicationFeed feed =
                   new SyndicationFeed("codingphobia.com Feed",
                                       "This is a feed from codingphobia.com",
                                       new Uri("http://codingphobia.com/feed/"),
                                       "somefeedID",
                                       DateTime.Now);
            SyndicationItem item1 =
                new SyndicationItem("Linq To XML: Generate Types with Nested Types using C#",
                                   "This is the content for Test item2",
                                    new Uri("http://codingphobia.com/2010/03/12/linq-to-xml-generate-types-with-nested-types-using-c/"),
                                   "TestItemID",
                                   DateTime.Now);

            List<SyndicationItem> items = new List<SyndicationItem>();

            items.Add(item1);
            feed.Items = items;

            return new FeedActionResult() { Feed = feed };
        }

As ActionResult is an abstract class, we need to provide implementation for the ExecuteResult method which is as follows

        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = "application/rss+xml";

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

Now we can call FeedActionResult.GetFeed() from our Controller, dedicated for processing requests for Feeds. Here is how we do this

    public class FeedController : Controller
    {      
        // GET: /Feed/
        public ActionResult Index(string format)
        {
            return new FeedActionResult().GetFeed();
        }
    }

I have used action method Index(string format) with as string parameter here. This will help us to receive requests, for both Atom and Rss based Formats. We will look into this in later Post.

Following is the route settings in the global.asx file

routes.MapRoute(
                "Feed", // Route name
                "{controller}/{action}/{type}", // URL with parameters
                new { controller = "Feed", action = "Index", type = UrlParameter.Optional } 
            );

DefaultModelBinder class in System.Web.Mvc

While working with validation for an  MVC application I came across option for using  DefaultModelBinder class.

public class DefaultModelBinder : IModelBinder

maps following types in response to a request.
primitive types, model classes and collections

What it does is, it takes the required object from the model, binds its properties to the corresponding HTML fields of the form (View).

If the class it is binding implements IDataErrorInfo interface, which binds the developer to implement following properties

Error:  string Error { get; }

Item :  string this[string columnName] { get; }

If a class implements the interface, it ftches the value for the fields, by invoking IDataErrorInfo.this indexer.

What you can do interesting is you can perform your validation / checks and over an object in focus and  if in case it fails to correspond with the requirements, like a customer has provided wrong value for the name of something, It will return the error value , custom provided  at the time of implementation of the interface. Have a look.

Here are details for DefaultModelBinder