feat: implement rss feed

This commit is contained in:
Stevan Freeborn
2024-04-12 19:31:56 -05:00
parent dd7e7e1783
commit 95d4888876
4 changed files with 107 additions and 1 deletions
+1
View File
@@ -2,6 +2,7 @@
"cSpell.words": [ "cSpell.words": [
"Antiforgery", "Antiforgery",
"blazor", "blazor",
"Entitize",
"Hsts", "Hsts",
"Markdig", "Markdig",
"msedge" "msedge"
+1
View File
@@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Markdig" Version="0.36.2" /> <PackageReference Include="Markdig" Version="0.36.2" />
<PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="21.0.2" /> <PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="21.0.2" />
<PackageReference Include="WebStoating.Markdig.Prism" Version="1.0.0" /> <PackageReference Include="WebStoating.Markdig.Prism" Version="1.0.0" />
</ItemGroup> </ItemGroup>
+68 -1
View File
@@ -1,3 +1,8 @@
using System.ServiceModel.Syndication;
using System.Text;
using System.Xml;
using System.Xml.Linq;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigureOptions<FilePostServiceOptionsSetup>(); builder.Services.ConfigureOptions<FilePostServiceOptionsSetup>();
@@ -21,11 +26,73 @@ app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseAntiforgery(); app.UseAntiforgery();
app.UseStatusCodePagesWithRedirects("/Error/{0}");
app
.MapGet("/rss", async (HttpContext context, IPostService postService) =>
{
var req = context.Request;
var url = $"{req.Scheme}://{req.Host}{req.PathBase}";
var posts = await postService.GetPostsAsync();
var items = posts.Select(post =>
{
var uri = new Uri($"{url}/{post.Slug}");
var item = new SyndicationItem(
post.Title,
post.Lead,
uri,
post.Slug,
post.PublishedAt
);
return item;
});
var feed = new SyndicationFeed(
"journal",
"A blog by Stevan Freeborn",
new Uri(url)
)
{
Items = items,
};
XNamespace atom = "http://www.w3.org/2005/Atom";
feed.ElementExtensions.Add(
new XElement(atom + "link",
new XAttribute("href", url + "/rss"),
new XAttribute("rel", "self"),
new XAttribute("type", "application/rss+xml")
)
);
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
NewLineHandling = NewLineHandling.Entitize,
Indent = true,
Async = true,
};
using var stream = new MemoryStream();
using var writer = XmlWriter.Create(stream, settings);
var rssFormatter = new Rss20FeedFormatter(feed, false);
rssFormatter.WriteTo(writer);
await writer.FlushAsync();
return Results.File(stream.ToArray(), "application/xml");
})
.WithDisplayName("RSS Feed")
.WithDescription("RSS feed for the blog");
app app
.MapRazorComponents<App>() .MapRazorComponents<App>()
.AddInteractiveServerRenderMode(); .AddInteractiveServerRenderMode();
app.UseStatusCodePagesWithRedirects("/Error/{0}");
app.Run(); app.Run();
+37
View File
@@ -0,0 +1,37 @@
using System.ServiceModel.Syndication;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace Blog.Tests.EndToEnd;
[TestFixture]
public class RssTests : BlogTest
{
[Test]
public async Task Task_Rss_WhenFetched_ItShouldReturnRssFeed()
{
var response = await Page.APIRequest.GetAsync("/rss");
response.Status.Should().Be((int)HttpStatusCode.OK);
var xml = await response.BodyAsync();
var streamReader = new StreamReader(
new MemoryStream(xml),
Encoding.UTF8
);
var settings = new XmlReaderSettings
{
Async = true
};
using var xmlReader = XmlReader.Create(streamReader, settings);
var feed = SyndicationFeed.Load(xmlReader);
feed.Title.Text.Should().Be("journal");
feed.Description.Text.Should().Be("A blog by Stevan Freeborn");
feed.Items.Should().HaveCount(2);
}
}