Ostatnio stanąłem oko w oko z zadaniem stworzenia kanału RSS. Spodziewałem się czegoś trudniejszego, a zadanie okazało się o wiele prostsze niż myślałem. Być może sposób ten się komuś przyda, więc na wszelki wypadek się nim podzielę.
Po szybkim researchu zdecydowanie najprzyjemniejszym i najprostszym rozwiązaniem wydało mi się stworzenie klasy RSSResult dziedziczącej po ActionResult, czyli klasie, którą zwraca praktycznie każdy kontroler. W połączeniu z namespacem System.ServiceModel.Syndication i kilkoma zapytaniami skleconymi w Entity Framework udało mi się postawić kanał i zapełnić go contentem bardzo szybko. Niestety nie zapisałem nigdzie linków z których czerpałem, ale nie będzie tu zbyt wiele wiedzy do uzupełnienia.
Ale zacznijmy od początku. Podstawą będzie zwracany przez akcję RSSResult, więc zajmijmy się nim w pierwszej kolejności. Musi to być klasa dziedzicząca po ActionResult i korzystająca z dobrodziejstw wspomnianej wcześniej przestrzeni nazw. Efekt finalny wyglądał mniej więcej w ten sposób:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System.ServiceModel.Syndication; public class RSSActionResult : ActionResult { private SyndicationFeed _feed { get; set; } public RSSActionResult(SyndicationFeed feed) { _feed = feed; } public override void ExecuteResult(ControllerContext context) { context.HttpContext.Response.ContentType = "application/rss+xml"; var formatter = new Rss20FeedFormatter(_feed); using (var writer = XmlWriter.Create(context.HttpContext.Response.Output)) { formatter.WriteTo(writer); } } } |
Sprawa jest stosunkowo prosta i nie ma tu żadnej wielkiej filozofii. Przepuszczamy obiekt SyndicationFeed przez Rss20FeedFormatter i zwyczajny XmlWriter, a nasz RSSActionResult wypluje coś, co zostanie odczytane przez czytniki RSS. Pozostaje tylko wypełnienie naszego kanału treścią i nie jest to ani trochę bardziej skomplikowane, bo robimy to dokładnie w ten sam sposób, w jaki budujemy standardową metodę akcji za wyjątkiem tego, że zwracamy powyższy RSSResult i zamiast ViewModelu przekazujemy utworzony i wypełniony contentem obiekt SyndicationFeed.
Akcja zwracająca nasz kanał RSS wygląda mniej więcej w ten sposób.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class RssController : Controller { DbContext db; public ActionResult Index() { var rssItems = new List<SyndicationItem>(); var newsContent = db.News.AsNoTracking().ToList(); newsContent .ForEach(n => rssItems.Add( new SyndicationItem(n.Title, n.Content, new Uri(n.Url), n.LastModificationDate ))); var feed = new SyndicationFeed("RSS Feed title", "Some description", new Uri("http://some.link.to.rss"), "Rss feed id", DateTime.Now, rssItems); return new RSSActionResult(feed); } } |
Wyciągamy więc dane z bazy w standardowy sposób, tworzymy kolekcję obiektów SyndicationItem, które są pojedynczymi elementami pojawiającymi się w kanale RSS, następnie tworzymy obiekt SyndicationFeed, przekazujemy mu różne dane personalizujące nasz feed i stworzoną wcześniej kolekcję. Tak stworzony obiekt zwracamy w RSSActionResult, a po wejściu w http://jakis.link/Rss naszym oczom powinna pojawić się zawartość kanału, która bez czytnika RSS jest raczej mało wygodna do przeglądania.
Pojedyncze obiekty SyndicationFeed i SyndicationItem mają jeszcze kilka innych właściwości, których nie ustawimy przekazując parametry w konstruktorze. Jedną z tych użyteczniejszych jest właściwość Authors, która pozwala podpiąć autora danego posta/newsa/czegokolwiek i wyświetlić w kanale na przykład jego adres mailowy. Warto przyjrzeć się tym dodatkowym właściwościom, bo dają one możliwość pełnej konfiguracji i personalizacji kanału RSS.