feat: implement rss feed
This commit is contained in:
Vendored
+1
@@ -2,6 +2,7 @@
|
|||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Antiforgery",
|
"Antiforgery",
|
||||||
"blazor",
|
"blazor",
|
||||||
|
"Entitize",
|
||||||
"Hsts",
|
"Hsts",
|
||||||
"Markdig",
|
"Markdig",
|
||||||
"msedge"
|
"msedge"
|
||||||
|
|||||||
@@ -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
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user