Monday, July 9, 2012

REST WCF Feed Service with Dynamic Response

Much like the project in which we built a WCF dynamic response service allowing for JSON, XML or DataContract serialization, this project is based on dynamic response capabilities.  In this project, we will continue with the Album services created previously and extend them to dynamic response as a syndicated feed.  This RESTful service will dynamically respond with either ATOM or RSS syndication based on the request type.

I will accomplish this using two different examples of response, one with a return type of System.ServiceModel.Channels.Message and another with a return type of SyndicationFeedFormatter.  First, we need to syndicate a feed based on our data repository.  I am including the ability to filter our feed based on genre and artist so we can access virtual sub-feeds with a uri.


private SyndicationFeed CreateAlbumFeed(String genre = "all", String artist = "all")
{
    SyndicationFeed feed = new SyndicationFeed
    {
        Title = SyndicationContent.CreatePlaintextContent("The album listing"),
        Description = SyndicationContent.CreatePlaintextContent("Dynamic feed response from single WCF"),
        LastUpdatedTime = DateTime.Now,
        Items = from a in DataRepository.Albums()
                where ((genre.ToLower().CompareTo("all") == 0)||(a.Genre == genre)) &&
                ((artist.ToLower().CompareTo("all") == 0) || (a.Artist == artist))
                select new SyndicationItem
                {
                    LastUpdatedTime = DateTime.Now,
                    Title = SyndicationContent.CreatePlaintextContent(a.Artist+": "+a.Name),
                    Content = SyndicationContent.CreateXmlContent(a)
                }
 
    };
    return feed;
}
 
 
This function will do nothing more than create a syndication feed from our album listing based on a couple of parameters.  Next, we need to add interface options for retrieving the feed.

 [OperationContract]
 [WebGet(UriTemplate = "/Feeds/Albums"), 
         Description("Returns an Atom or RSS feed of albums")]
 System.ServiceModel.Channels.Message GetAlbumFeed();
 
 [OperationContract]
 [WebGet(UriTemplate = "/Feeds/Genre/{genre}"), 
         Description("Returns an Atom or RSS feed of albums by genre")]
 SyndicationFeedFormatter GetGenreFeed(String genre);
 
 [OperationContract]
 [WebGet(UriTemplate = "/Feeds/Artist/{artist}"), 
         Description("Returns an Atom or RSS feed of albums by artist")]
 SyndicationFeedFormatter GetArtistFeed(String artist);

The return type of System.ServiceModel.Channels.Message is very generic and allows for you to simply build the response of a web request.  While generic and flexible, it does limit you by not allowing parameters on the REST uri.  In order to allow a SyndicationFeedFormatter return type, we must ensure that the service can serialize it as a result.  This is achieved by making it a known type for the service by decorating the interface as follows.

[ServiceKnownType(typeof(Atom10FeedFormatter))]
[ServiceKnownType(typeof(Rss20FeedFormatter))]

At this point, it comes down to dynamically formatting your response.  We do this by implicitly checking the Content-Type value of the request header in the body of our service methods.  Here is one example:

public SyndicationFeedFormatter GetGenreFeed(String genre)
{
    SyndicationFeed feed = CreateAlbumFeed(genre: genre);
    if (WebOperationContext.Current.IncomingRequest.Headers.Get("Content-Type") != null)
    {
        if (WebOperationContext.Current.IncomingRequest.Headers.Get("Content-Type").ToLower().Contains("application/atom"))
            return new Atom10FeedFormatter(feed);
        else
            return new Rss20FeedFormatter(feed);
    }
    return new Rss20FeedFormatter(feed);
}
 
 
This dictates how the feed will be formatted upon return from the function.  You can check the output using fiddler to see the message itself, as well as configure the content type for dynamism.



 Now we are able to browse to our URI and see a feed for all albums, albums by artist and albums by genre. 



The added ability to select the method in which the client consumes your feed is valuable to ensure all consumers are included.  Download the project here