add: 渲染前处理器和渲染后处理器
This commit is contained in:
parent
83a9a06fec
commit
2c6414dbea
10
YaeBlog.Core/Abstractions/IPostRenderProcessor.cs
Normal file
10
YaeBlog.Core/Abstractions/IPostRenderProcessor.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using YaeBlog.Core.Models;
|
||||||
|
|
||||||
|
namespace YaeBlog.Core.Abstractions;
|
||||||
|
|
||||||
|
public interface IPostRenderProcessor
|
||||||
|
{
|
||||||
|
BlogEssay Process(BlogEssay essay);
|
||||||
|
|
||||||
|
string Name { get; }
|
||||||
|
}
|
10
YaeBlog.Core/Abstractions/IPreRenderProcessor.cs
Normal file
10
YaeBlog.Core/Abstractions/IPreRenderProcessor.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using YaeBlog.Core.Models;
|
||||||
|
|
||||||
|
namespace YaeBlog.Core.Abstractions;
|
||||||
|
|
||||||
|
public interface IPreRenderProcessor
|
||||||
|
{
|
||||||
|
BlogContent Process(BlogContent content);
|
||||||
|
|
||||||
|
string Name { get; }
|
||||||
|
}
|
47
YaeBlog.Core/Extensions/BlogApplicationBuilderExtension.cs
Normal file
47
YaeBlog.Core/Extensions/BlogApplicationBuilderExtension.cs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
using Markdig;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using YaeBlog.Core.Builder;
|
||||||
|
using YaeBlog.Core.Models;
|
||||||
|
using YaeBlog.Core.Services;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
|
|
||||||
|
namespace YaeBlog.Core.Extensions;
|
||||||
|
|
||||||
|
public static class BlogApplicationBuilderExtension
|
||||||
|
{
|
||||||
|
internal static void ConfigureBlogApplication(this BlogApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json",
|
||||||
|
optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddEnvironmentVariables();
|
||||||
|
|
||||||
|
builder.Services.Configure<BlogOptions>(
|
||||||
|
builder.Configuration.GetSection(BlogOptions.OptionName));
|
||||||
|
|
||||||
|
builder.YamlDeserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
|
||||||
|
builder.YamlDeserializerBuilder.IgnoreUnmatchedProperties();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<MarkdownPipeline>(
|
||||||
|
_ => builder.MarkdigPipelineBuilder.Build());
|
||||||
|
builder.Services.AddSingleton<IDeserializer>(
|
||||||
|
_ => builder.YamlDeserializerBuilder.Build());
|
||||||
|
|
||||||
|
builder.Services.AddHostedService<BlogHostedService>();
|
||||||
|
builder.Services.AddSingleton<EssayScanService>();
|
||||||
|
builder.Services.AddSingleton<RendererService>();
|
||||||
|
builder.Services.AddSingleton<EssayContentService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ConfigureWebApplication(this BlogApplicationBuilder builder,
|
||||||
|
Action<WebApplicationBuilder> configureWebApplicationBuilder,
|
||||||
|
Action<WebApplication> configureWebApplication)
|
||||||
|
{
|
||||||
|
builder.Services.AddHostedService<WebApplicationHostedService>(provider =>
|
||||||
|
new WebApplicationHostedService(configureWebApplicationBuilder,
|
||||||
|
configureWebApplication, provider));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +1,29 @@
|
||||||
using Markdig;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using YaeBlog.Core.Abstractions;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using YaeBlog.Core.Builder;
|
using YaeBlog.Core.Builder;
|
||||||
using YaeBlog.Core.Models;
|
|
||||||
using YaeBlog.Core.Services;
|
using YaeBlog.Core.Services;
|
||||||
using YamlDotNet.Serialization;
|
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
|
||||||
|
|
||||||
namespace YaeBlog.Core.Extensions;
|
namespace YaeBlog.Core.Extensions;
|
||||||
|
|
||||||
public static class BlogApplicationExtension
|
public static class BlogApplicationExtension
|
||||||
{
|
{
|
||||||
internal static void ConfigureBlogApplication(this BlogApplicationBuilder builder)
|
public static void UsePreRenderProcessor<T>(this BlogApplication application)
|
||||||
|
where T : IPreRenderProcessor
|
||||||
{
|
{
|
||||||
builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
|
RendererService rendererService =
|
||||||
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json",
|
application.Services.GetRequiredService<RendererService>();
|
||||||
optional: true, reloadOnChange: true);
|
T preRenderProcessor =
|
||||||
builder.Configuration.AddEnvironmentVariables();
|
application.Services.GetRequiredService<T>();
|
||||||
|
rendererService.AddPreRenderProcessor(preRenderProcessor);
|
||||||
builder.Services.Configure<BlogOptions>(
|
|
||||||
builder.Configuration.GetSection(BlogOptions.OptionName));
|
|
||||||
|
|
||||||
builder.YamlDeserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
|
|
||||||
builder.YamlDeserializerBuilder.IgnoreUnmatchedProperties();
|
|
||||||
|
|
||||||
builder.Services.AddSingleton<MarkdownPipeline>(
|
|
||||||
_ => builder.MarkdigPipelineBuilder.Build());
|
|
||||||
builder.Services.AddSingleton<IDeserializer>(
|
|
||||||
_ => builder.YamlDeserializerBuilder.Build());
|
|
||||||
|
|
||||||
builder.Services.AddHostedService<BlogHostedService>();
|
|
||||||
builder.Services.AddSingleton<EssayScanService>();
|
|
||||||
builder.Services.AddSingleton<RendererService>();
|
|
||||||
builder.Services.AddSingleton<EssayContentService>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConfigureWebApplication(this BlogApplicationBuilder builder,
|
public static void UsePostRenderProcessor<T>(this BlogApplication application)
|
||||||
Action<WebApplicationBuilder> configureWebApplicationBuilder,
|
where T : IPostRenderProcessor
|
||||||
Action<WebApplication> configureWebApplication)
|
|
||||||
{
|
{
|
||||||
builder.Services.AddHostedService<WebApplicationHostedService>(provider =>
|
RendererService rendererService =
|
||||||
new WebApplicationHostedService(configureWebApplicationBuilder,
|
application.Services.GetRequiredService<RendererService>();
|
||||||
configureWebApplication, provider));
|
T postRenderProcessor =
|
||||||
|
application.Services.GetRequiredService<T>();
|
||||||
|
rendererService.AddPostRenderProcessor(postRenderProcessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using System.Diagnostics;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using Markdig;
|
using Markdig;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using YaeBlog.Core.Abstractions;
|
||||||
using YaeBlog.Core.Exceptions;
|
using YaeBlog.Core.Exceptions;
|
||||||
using YaeBlog.Core.Models;
|
using YaeBlog.Core.Models;
|
||||||
using YamlDotNet.Core;
|
using YamlDotNet.Core;
|
||||||
|
@ -16,18 +18,23 @@ public class RendererService(ILogger<RendererService> logger,
|
||||||
{
|
{
|
||||||
private readonly Stopwatch _stopwatch = new();
|
private readonly Stopwatch _stopwatch = new();
|
||||||
|
|
||||||
|
private readonly List<IPreRenderProcessor> _preRenderProcessors = [];
|
||||||
|
|
||||||
|
private readonly List<IPostRenderProcessor> _postRenderProcessors = [];
|
||||||
|
|
||||||
public async Task RenderAsync()
|
public async Task RenderAsync()
|
||||||
{
|
{
|
||||||
_stopwatch.Start();
|
_stopwatch.Start();
|
||||||
logger.LogInformation("Render essays start.");
|
logger.LogInformation("Render essays start.");
|
||||||
|
|
||||||
List<BlogContent> contents = await essayScanService.ScanAsync();
|
List<BlogContent> contents = await essayScanService.ScanAsync();
|
||||||
|
IEnumerable<BlogContent> preProcessedContents =
|
||||||
|
PreProcess(contents);
|
||||||
|
|
||||||
List<BlogEssay> essays = [];
|
List<BlogEssay> essays = [];
|
||||||
|
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
foreach (BlogContent content in contents)
|
foreach (BlogContent content in preProcessedContents)
|
||||||
{
|
{
|
||||||
MarkdownMetadata? metadata = TryParseMetadata(content);
|
MarkdownMetadata? metadata = TryParseMetadata(content);
|
||||||
BlogEssay essay = new()
|
BlogEssay essay = new()
|
||||||
|
@ -46,6 +53,7 @@ public class RendererService(ILogger<RendererService> logger,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ConcurrentBag<BlogEssay> postProcessEssays = [];
|
||||||
Parallel.ForEach(essays, essay =>
|
Parallel.ForEach(essays, essay =>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -58,19 +66,79 @@ public class RendererService(ILogger<RendererService> logger,
|
||||||
};
|
};
|
||||||
newEssay.Tags.AddRange(essay.Tags);
|
newEssay.Tags.AddRange(essay.Tags);
|
||||||
|
|
||||||
if (!essayContentService.TryAdd(newEssay.FileName, newEssay))
|
postProcessEssays.Add(newEssay);
|
||||||
{
|
|
||||||
throw new BlogFileException(
|
|
||||||
$"There are two essays with the same name: '{newEssay.FileName}'.");
|
|
||||||
}
|
|
||||||
logger.LogDebug("Render markdown file {}.", newEssay);
|
logger.LogDebug("Render markdown file {}.", newEssay);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PostProcess(postProcessEssays);
|
||||||
|
|
||||||
_stopwatch.Stop();
|
_stopwatch.Stop();
|
||||||
logger.LogInformation("Render finished, consuming {} s.",
|
logger.LogInformation("Render finished, consuming {} s.",
|
||||||
_stopwatch.Elapsed.ToString("s\\.fff"));
|
_stopwatch.Elapsed.ToString("s\\.fff"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddPreRenderProcessor(IPreRenderProcessor processor)
|
||||||
|
{
|
||||||
|
bool exist = _preRenderProcessors.Any(p => p.Name == processor.Name);
|
||||||
|
|
||||||
|
if (exist)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("There exists one pre-render processor " +
|
||||||
|
$"with the same name: {processor.Name}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_preRenderProcessors.Add(processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddPostRenderProcessor(IPostRenderProcessor processor)
|
||||||
|
{
|
||||||
|
bool exist = _postRenderProcessors.Any(p => p.Name == processor.Name);
|
||||||
|
|
||||||
|
if (exist)
|
||||||
|
{
|
||||||
|
throw new InvalidCastException("There exists one post-render processor " +
|
||||||
|
$"with the same name: {processor.Name}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_postRenderProcessors.Add(processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<BlogContent> PreProcess(IEnumerable<BlogContent> contents)
|
||||||
|
{
|
||||||
|
ConcurrentBag<BlogContent> processedContents = [];
|
||||||
|
|
||||||
|
Parallel.ForEach(contents, content =>
|
||||||
|
{
|
||||||
|
foreach (IPreRenderProcessor processor in _preRenderProcessors)
|
||||||
|
{
|
||||||
|
content = processor.Process(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
processedContents.Add(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
return processedContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PostProcess(IEnumerable<BlogEssay> essays)
|
||||||
|
{
|
||||||
|
Parallel.ForEach(essays, essay =>
|
||||||
|
{
|
||||||
|
foreach (IPostRenderProcessor processor in _postRenderProcessors)
|
||||||
|
{
|
||||||
|
essay = processor.Process(essay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!essayContentService.TryAdd(essay.FileName, essay))
|
||||||
|
{
|
||||||
|
throw new BlogFileException(
|
||||||
|
$"There are two essays with the same name: '{essay.FileName}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("Post-Process essay: {}.", essay);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private MarkdownMetadata? TryParseMetadata(BlogContent content)
|
private MarkdownMetadata? TryParseMetadata(BlogContent content)
|
||||||
{
|
{
|
||||||
string fileContent = content.FileContent.Trim();
|
string fileContent = content.FileContent.Trim();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user