Exposer un flux RSS/Atom avec MVC

Voici le dernier article sur ma série dédiée à la mise à disposition de flux RSS/Atom. Arpès Web API, attaquons la version MVC.

Un flux RSS ou Atom a pour vocation de retourner des informations à des clients qui ne formalise pas foncement toujours bien ce qu’il attend. La plus pars du temps, le client fait un simple Get sur une url fixe sans arguments et sens stipuler le format attendu. MVC est donc tout indiqué pour ce type d’opération.

Les plus anciens préfèreront le bon vieux Handler (ashx) ou la page aspx. Les rares vrais connaisseurs de WCF préfèreront utiliser son contrat dédié…. Il y en a pour tout les goûts. La solution MVC n’est donc pas la seule solution viable et simple. MVC étant pour autant de plus en plus utilisé, il semble bon de monter ce que l’on peut faire avec.

Comme pour la solution Web API, la version MVC est en deux parties :

  1. Un Controller qui va répondre aux requêtes et retourner une instance de SyndicationFeed (la classe intégrée à .net Framework)
  2. Un ActionResult qui va se charger de formater le SyndicationFeed en XML respectant les formats RSS et Atom.

Pour simplifier mon code, j’ai réalisé une méthode statique qui me retourne une instance de SyndicationFeed . La classe est fortement liée à ma couche de donnée. Je la présente ici uniquement pour mettre en avant les éléments qui sont utiles pour avoir une instance de SyndicationFeed  exploitable.


/// <summary>
/// Blog Feed (for RSS or ATOM)
/// </summary>
public sealed class FeedService
{
    /// <summary>
    /// Get the blog feed
    /// </summary>
    /// <returns></returns>
    public static SyndicationFeed Get()
    {
        SyndicationFeed feed = new SyndicationFeed(
        Settings.Current.Title,
        Settings.Current.SubTitle,
        new Uri(Settings.Current.Url));

        using (var db = new DataService())
        {
            var posts = db.GetPosts(0, 10);
            if (posts != null && posts.Length > 0)
            {
                // Create a new list of items
                var items = posts.Select(c =>
                    new SyndicationItem(
                        c.Title,
                        new TextSyndicationContent(
                            c.HtmlSummary, 
                            TextSyndicationContentKind.XHtml),
                        new Uri(c.Url),
                        c.Id.ToString(),
                        c.DateCreatedGmt)
                ).ToArray();
                // Add items
                feed.Items = items;
            }
        }
        return feed;
    }
}


Ce code permet d’avoir un Controller très simple. Celui-ci a 3 actions :

  1. Pour retourner le flux au format RSS par défaut lors de l’appel de l’url /Feed
  2. Pour retourner le flux au format RSS lors de l’appel  de l’url /Feed/Rss
  3. Pour retourner le flux au format Atom lors de l’appel  de l’url /Feed/Atom.

public class FeedController : Controller
{
    public ActionResult Index()
    {
        return new FeedResult(FeedService.Get(), FeedResult.Type.Rss);
    }

    public ActionResult Atom()
    {
        return new FeedResult(FeedService.Get(), FeedResult.Type.Atom);
    }

    public ActionResult Rss()
    {
        return new FeedResult(FeedService.Get(), FeedResult.Type.Rss);
    }
}


Côté ActionResult, j’utilise les formatters Atom10FeedFormatter et Rss20FeedFormatter. Le code résultant est donc des plus simples.


public sealed class FeedResult : ActionResult
{
    #region Declarations

    private const String AtomContentType = "application/atom+xml";
    private const String RssContentType= "application/rss+xml";

    public enum Type
    {            
        Atom,
        Rss
    }

    private readonly SyndicationFeed _feed;
    private readonly Type _type;

    #endregion

    #region Constructors

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="feed"></param>
    public FeedResult(SyndicationFeed feed, Type type)
    {
        _feed = feed;
        _type = type;
    }

    #endregion

    #region Methodes

    public override void ExecuteResult(ControllerContext context)
    {
        // Get the response
        HttpResponseBase response = context.HttpContext.Response;

        // Add header en encoding
        response.ContentEncoding = Encoding.UTF8;

        var settings = new XmlWriterSettings
        {
            Encoding = new UTF8Encoding(false)
        };
        using (XmlWriter writer = XmlWriter.Create(response.Output, settings))
        {
            if (_type==Type.Atom)
            {
                Atom10FeedFormatter atomformatter = new Atom10FeedFormatter(_feed);
                atomformatter.WriteTo(writer);
            }
            else
            {
                Rss20FeedFormatter rssformatter = new Rss20FeedFormatter(_feed);
                rssformatter.WriteTo(writer);
            }
        }
    }

    #endregion
}

Je crois que cela se passe de commentaires supplémentaires.

Facile non?

Jérémy Jeanson

Comments

You have to be logged in to comment this post.