RSS & Atom MediaTypeFormatter for ASP.NET WebAPI - StrathWeb

Strath

April 22nd, 2012

RSS & Atom MediaTypeFormatter for ASP.NET WebAPI

Because WebAPI can easily format responses to whatever you want

Today we are going to build a custom formatter for ASP.NET WebAPI, deriving from MediaTypeFormatter class. It will return our model (or collection of models) in RSS or Atom format.
The Formatter is supposed to react to requests sent with request headers “Accept: application/atom+xml” and “Accept: application/rss+xml”.

Let’s get going.

Update – September 2012

This post was originally written against Web API beta. Since then the RC and then RTM versions have been released. Below is the code updated for the RTM version (the “proper” Web API released on 15th August).

Starting off

We will start as always when dealing with WebAPI – by creating a new ASP.NET MVC 4 project, and choosing WebAPI template. Make sure you are using Visual Studio 11 beta, cause the source files included in this post are VS11.

Model

Our model is looking like this:

For simplicity, we will skip the DB altogether in this exercise, and use the usual repository pattern. The interface is as follows:

And the repository class as follows. Note that in the constructor we instantiate two objects into the collection so that we have some data to work with. Also, we omitted the Remove() and Update() methods, as they are irrelevant for our today’s examples.

ApiController

Our ApiController also isn’t going to be anything sophisticated. In fact, for the purposes of this exercise, we are only interested in Get() and Get(int id) methods.

Basically all the ApiController does is calls the UrlRepository methods to retrieve the specific object instances. Notice that the controller itself doesn’t worry about any serialization or formatting (instead, just returns normal models). This – our core activity for today – is handled elsewhere.

Setting up the MediaTypeFormatter

Now that we have all the building blocks of the sample web application in place, we can start with our MediaTypeFormatter. I will be deriving from the MediaTypeFormatter class, but you might as well derive your formatter from BufferedMediaTypeFormatter. The difference is that MediaTypeFormatter wraps the read/write methods in asynchrounous methods, while BufferedMediaTypeFormatter uses synchronous ones.

Let’s add a class SyndicationFeedFormatter.cs to our project, and inherit from MediaTypeFormatter.

We will be playing around with application/atom+xml and application/rss+xml MIME types, so we might as well put them in some reusable properties.

In the constructor, we need to tell the formatter that these are the MIME types it should look for in the headers of the request.

Next step is to tell the formatter which custom types (models) to process. To do that, we need to override CanWriteType() method of MediaTypeFormatter. In our case, we will support Url type and IEnumerable of Url. If you wish to support all types, you can always simply return true from this method.

One more override we need to include is OnWriteToStreamAsync(), in which we will tell the formatter how to write the response to the client. This is where we will build up the actual RSS or Atom structure out of our models.

In this method, we check if the type is one of our supported types, and we call the BuildSyndicationFeed() private method, which will do all the magic.

This is what happens in the method:
1. We create an instance of SyndicationFeed. This is going to be then returned as either Atom- or RSS-structured XML, depending on what the client originally requested via the Headers.
2. We check if the object passed from the OnWriteToStreamAsync() is either Url type or IEnumerable of Url. Depedning on that, we’d have to treat it differently.
2a. If it’s IEnumerable, we iterate through the IEnumerable and call BuildSyndicationItem() for each Url instance.
2b. If it’s just a single Url object, then we call that method just once.
3. BuildSyndicationItem(), into which we’ll look in a moment, returns a SyndicationItem object, which we add to the Items property of the SyndicationFeed instance created beforehand.
4. Using the XmlWriter, we write either RSS or Atom feed (depending on the Content Type requested by the client) to the Stream that is going to be flushed as the Response.

The last thing to look at is the BuildSyndiactionItem() method, which transforms our model (Url) into SyndicationItem object.

Hooking up the formatter to GlobalConfiguration

Finally, we need to add our formatter to GlobalConfiguration in the Global.asax. GlobalConfiguration.Configuration exposes a Formatters collection, which contains all formatters. To include ours there, we add the following line in the Application_Start():

Trying it out

In order to try this out, we’ll add a couple of jQuery $.ajax calls to the Index.cshtml. First let’s try the Atom feed for all items:

This produces the following output:

Now, let’s try RSS for all items:

Also working as expected:

What about Atom and RSS for single item?

First, Atom:

And RSS:

Summary and source code

As you see it’s quite easy to build your own media type formatters for your ApiControllers. In fact, Gunnar Peipman has written a terrific blog post on extending content negotiation in WebApi here.

In our case, we have created both RSS and Atom formats using one formatter, supporting both a single model and an IEnumerable of model. The only downside of this solution, is that the client needs to specifically ask for MIME application/atom+xml and application/rss+xml. Therefore it’s suitable for RSS/Atom calls made programmatically somewhere from the backend, but not for accessing the feed via the browser, or using the client that doesn’t pass the Content Type headers correctly. In the next post, I’ll show how to work around it, and use this custom formatter also in regular GET calls triggered from the browser or, for that matter, anywhere else.

In the meantime, as always:
– source code (VS11 beta project), ZIP, 6.5MB

Be Sociable, Share!

  • Pingback: Dew Drop – April 23, 2012 (#1,311) | Alvin Ashcraft's Morning Dew

  • James Hancock

    Excellent post!

    A couple of questions:

    1. Is this in OData compliant Atom format?

    2. Suggestions on getting out OData compliant json?

    • Filip W

      James, this example here is just Atom, and OData is built on top of AtomPub, which is built on top of Atom :-) .

      To answer both of your questions, you’d need to write your own MediatypeFormatter, using similar technique as shown above, which implements Microsoft.Net.Formatting.OData. Some examples for WCF Web API can be found here http://wcf.codeplex.com/SourceControl/changeset/changes/8549b55d3ff9. Most of the code should be reusable (to a degree) with ASP.NET WebAPI.

      Also, if you want to just use oData for CRUD operations (not returning oData compliant formats), then, obviously, this is supported in WebAPI out of the box. Check out Jon’s video here http://www.asp.net/web-api/videos/getting-started/paging-and-querying

  • http://www.dotnetjalps.com Jalpesh Vadgama

    Nice work!!. I was working on asp.net mvc 4.0 and I have found the same what I was looking for.. Thanks for sharing

  • Pingback: friday links 28 « A Programmer with Microsoft tools

  • WD

    Major thanks for the article. Will read on…

  • Happy Harry

    cheers for your super input :D

  • Pingback: Link Resource # 62 : Jul 09 – Jul 16 « Dactylonomy of Web Resource

  • damien

    Looking forward to reading more. Great blog.Really thank you! Much obliged.

  • http://www.thinqlinq.com Jim Wooley

    I’m getting an access denied trying to access the dropbox for the sample project. Also, I’m finding that the CanWriteType method is getting called twice by WebApi: once when checking the IEnumerable type and a second time checking an anonymous type wrapper. The second call is failing and not returning results. I don’t know if this is caused by a newer build of WebApi than your samples were created on or not.

  • Exie Ferrera

    I don’t usually comment but I gotta state thank you for the post on this perfect one :D .

  • Steve Hueners

    Appreciate the post – it’s precisely what I’m looking for (webapi to rss) but the RC & RTM version of WebAPI break your code. Hoping you can suggest an update since this post rises to google’s top on related searches. http://stackoverflow.com/questions/12437731/create-rss-feed-in-mvc4-webapi

    again…many thx for the time

    • Filip W

      Thanks Steve. I posted the updated version of the code in your SO question.

      • Steve Hueners

        highly re-appreciated -thx

  • Steven SUing

    Your solution is good for a simple list, but you don’t address the hypermedia aspect of AtomPub. Do you know of an example that does that? I think I can extend your example to add Next, Previous, and First links, but was wondering if something was already out there.

    • Filip W

      Yeah, I think at the moment, there is nothing out there – you’d need to write your own custom stuff.
      I know for a fact that Ben – (http://ben.onfabrik.com) was planning to write something about AtomPub + WebAPI this week, but not sure what’s the status of it. You can reach out to him on Twitter.

  • Simon Segal

    Did you ever write that follow up post on getting it to work when a browser or anything else (i.e. not anything programmatic)?

  • Simon Segal

    Did you ever write that follow up post on getting it to work when a browser or anything else (i.e. not anything programmatic)?

  • Patrick

    Thanks for this tutorial. Unfortunately I am faced with an outlook error when trying to add “http://localhost:56661/api/values” as a RSS feed, saying that the feed is invalid. Any ideas?

  • Pingback: Using Protobuf-Net Media Formatter with Web Api 2 | Software Engineering