Utiliser Rss20FeedFormatter et Atom10FeedFormatter avec ASP .net core 3
De base si l'on tente de générer un flux RSS via ASP .net core 3 et un SyndicationFeed, on utilise un code synchrone. Ce qui n'est pas du gout de core 3 et qui va produire des Exceptions.
Voici le code d'un ActionResult personnalisé qui fonctionne parfaitement avec .net core 2.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.ServiceModel.Syndication;
using System.Text;
using System.Xml;
namespace MyLib.Web.Results
{
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(ActionContext context)
{
// Get the response
HttpResponse response = context.HttpContext.Response;
// Add settings
var settings = new XmlWriterSettings
{
Encoding = new UTF8Encoding(false)
};
// Write
using (XmlWriter writer = XmlWriter.Create(response.Body, settings))
{
if (_type == Type.Atom)
{
response.ContentType = AtomContentType;
Atom10FeedFormatter atomformatter = new Atom10FeedFormatter(_feed);
atomformatter.WriteTo(writer);
}
else
{
response.ContentType = RssContentType;
Rss20FeedFormatter rssformatter = new Rss20FeedFormatter(_feed);
rssformatter.WriteTo(writer);
}
}
}
#endregion
}
}
Pour l'utiliser en réponse d'un Controller, rien de très compliqué
return new FeedResult(yourFeed, FeedResult.Type.Rss);
Pour rendre ce code opérationnel avec .net core 3, il faut que la réponse soit produite par une méthode Async. La solution la plus simple et d'utiliser la méthode WriteAsync de Response et de lui passer une String. Pour cela, on peut passer par un StringBuilder qui va récupérer le produit du XmlWriter. Et pour rester dans l'asynchrone, on peut lui demander d'utiliser ces options asynchrones.
Ce qui donne :
public override async void ExecuteResult(ActionContext context)
{
// Get the response
HttpResponse response = context.HttpContext.Response;
// Add settings
var settings = new XmlWriterSettings
{
Encoding = new UTF8Encoding(false),
Async = true
};
// Write
String content;
using (StringWriter stringWriter = new StringWriter())
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings))
{
if (_type == Type.Atom)
{
response.ContentType = AtomContentType;
Atom10FeedFormatter formatter = new Atom10FeedFormatter(_feed);
formatter.WriteTo(xmlWriter);
}
else
{
response.ContentType = RssContentType;
Rss20FeedFormatter formatter = new Rss20FeedFormatter(_feed);
formatter.WriteTo(xmlWriter);
}
await xmlWriter.FlushAsync();
content = stringWriter.ToString();
}
await response.WriteAsync(content);
}
Voilà ;)