-
-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathRssFeedController.cs
111 lines (98 loc) · 3.79 KB
/
RssFeedController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using LinkDotNet.Blog.Domain;
using LinkDotNet.Blog.Infrastructure.Persistence;
using LinkDotNet.Blog.Web.Features;
using Microsoft.AspNetCore.Mvc;
namespace LinkDotNet.Blog.Web.Controller;
public sealed class RssFeedController : ControllerBase
{
private static readonly XmlWriterSettings Settings = CreateXmlWriterSettings();
private readonly AppConfiguration appConfiguration;
private readonly IRepository<BlogPost> blogPostRepository;
public RssFeedController(AppConfiguration appConfiguration, IRepository<BlogPost> blogPostRepository)
{
this.appConfiguration = appConfiguration;
this.blogPostRepository = blogPostRepository;
}
[ResponseCache(Duration = 1200)]
[HttpGet]
[Route("feed.rss")]
public async Task<IActionResult> GetRssFeed()
{
var url = $"{Request.Scheme}://{Request.Host}{Request.PathBase}";
var introductionDescription = MarkdownConverter.ToPlainString(appConfiguration.Introduction?.Description);
var feed = new SyndicationFeed(appConfiguration.BlogName, introductionDescription, new Uri(url))
{
Items = await GetBlogPostItems(url),
};
using var stream = new MemoryStream();
await WriteRssInfoToStreamAsync(stream, feed);
return File(stream.ToArray(), "application/rss+xml; charset=utf-8");
}
private static async Task WriteRssInfoToStreamAsync(Stream stream, SyndicationFeed feed)
{
await using var xmlWriter = XmlWriter.Create(stream, Settings);
var rssFormatter = new Rss20FeedFormatter(feed, false);
rssFormatter.WriteTo(xmlWriter);
await xmlWriter.FlushAsync();
}
private static XmlWriterSettings CreateXmlWriterSettings()
{
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
NewLineHandling = NewLineHandling.Entitize,
Indent = true,
Async = true,
};
return settings;
}
private static SyndicationItem CreateSyndicationItemFromBlogPost(string url, BlogPostRssInfo blogPost)
{
var blogPostUrl = url + $"/blogPost/{blogPost.Id}";
var shortDescription = MarkdownConverter.ToPlainString(blogPost.ShortDescription);
var item = new SyndicationItem(
blogPost.Title,
shortDescription,
new Uri(blogPostUrl),
blogPost.Id,
blogPost.UpdatedDate)
{
PublishDate = blogPost.UpdatedDate,
LastUpdatedTime = blogPost.UpdatedDate,
ElementExtensions = { new XElement("image", blogPost.PreviewImageUrl) },
};
AddCategories(item.Categories, blogPost);
return item;
}
private static void AddCategories(ICollection<SyndicationCategory> categories, BlogPostRssInfo blogPost)
{
foreach (var tag in blogPost.Tags ?? Array.Empty<string>())
{
categories.Add(new SyndicationCategory(tag));
}
}
private async Task<IEnumerable<SyndicationItem>> GetBlogPostItems(string url)
{
var blogPosts = await blogPostRepository.GetAllByProjectionAsync(
s => new BlogPostRssInfo(s.Id, s.Title, s.ShortDescription, s.UpdatedDate, s.PreviewImageUrl, s.Tags),
f => f.IsPublished,
orderBy: post => post.UpdatedDate);
return blogPosts.Select(bp => CreateSyndicationItemFromBlogPost(url, bp));
}
private sealed record BlogPostRssInfo(
string Id,
string Title,
string ShortDescription,
DateTime UpdatedDate,
string PreviewImageUrl,
IReadOnlyCollection<string> Tags);
}