当前位置: 首页 > news >正文

ASP.NET Core 中实现 Markdown 渲染中间件

文章目录

  • 前言
  • 一、核心功能
  • 二、实现步骤
    • 1)安装依赖包
    • 2)创建中间件类
    • 3)中间件扩展方法
    • 4)在Program.cs配置
    • 5)模板文件示例
    • 6)*.md文件示例
    • 7)缓存优化
    • 8)使用示例
  • 三、注意事项
  • 总结


前言

Markdown 内容的动态渲染,适用于文档系统、博客引擎等场景。

一、核心功能

  • 自动识别请求路径:将 .md.markdown 结尾的请求视为 Markdown 文件请求。

  • 实时转换:将 Markdown 内容转换为 HTML

  • 支持模板嵌入:将渲染后的 HTML 嵌入统一布局模板。

  • 异常处理:处理文件不存在或转换错误。

二、实现步骤

1)安装依赖包

  1. 使用 NuGet 安装 Markdown 解析库(推荐 Markdig):
    Install-Package Markdig
    

2)创建中间件类

  1. MarkdownRenderingMiddleware.cs
    using Microsoft.Extensions.FileProviders;namespace MarkDownMiddleware.Middleware
    {public class MarkdownRenderingMiddleware{private readonly RequestDelegate next;private readonly IFileProvider _fileProvider;private readonly string _template;public MarkdownRenderingMiddleware(RequestDelegate next, IFileProvider fileProvider, string template=null){this.next = next;_fileProvider = fileProvider;_template = template ?? "<html><body>{0}</body></html>";}public async Task InvokeAsync(HttpContext context){var path=context.Request.Path.Value;if (!path.EndsWith(".md")&&!path.EndsWith(".markdown")){await next(context);return;}var fileInfo=_fileProvider.GetFileInfo(path);if (!fileInfo.Exists){context.Response.StatusCode = 404;await context.Response.WriteAsync($"Markdown file ({path}) not found");return;}// 读取 Markdown 内容using var stream = fileInfo.CreateReadStream();using var reader = new StreamReader(stream);var markdown = await reader.ReadToEndAsync();// 转换为 HTMLvar html = Markdig.Markdown.ToHtml(markdown);// 嵌入模板var fullHtml = string.Format(_template, html);// 返回响应context.Response.ContentType = "text/html";await context.Response.WriteAsync(fullHtml);}}
    }
    

3)中间件扩展方法

  1. MarkdownRenderingMiddlewareExtensions.cs
    using MarkDownMiddleware.Middleware;
    using Microsoft.Extensions.FileProviders;namespace MarkDownMiddleware.Extensions
    {public static class MarkdownRenderingMiddlewareExtensions{public static IApplicationBuilder UseMarkdownRendering(this IApplicationBuilder app,string templatePath = null,string fileProviderRoot="wwwroot"){var fileProvider=new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(),fileProviderRoot));string template = null;if (!string.IsNullOrEmpty(templatePath)){var templateFile=fileProvider.GetFileInfo(templatePath);if (templateFile.Exists){using var stream=templateFile.CreateReadStream();using var reader=new StreamReader(stream);template = reader.ReadToEnd();}}return app.UseMiddleware<MarkdownRenderingMiddleware>(fileProvider,template);}}
    }
    

4)在Program.cs配置

  1. Program.cs
    using MarkDownMiddleware.Extensions;
    using MarkDownMiddleware.Middleware;
    using Microsoft.AspNetCore.Mvc.ApplicationParts;
    using Microsoft.Extensions.FileProviders;var builder = WebApplication.CreateBuilder(args);// Add services to the container.
    builder.Services.AddControllersWithViews();
    // 注册 IFileProvider 服务(指向 wwwroot 目录)
    builder.Services.AddSingleton<IFileProvider>(new PhysicalFileProvider(builder.Environment.WebRootPath)
    );
    var app = builder.Build();if (!app.Environment.IsDevelopment())
    {app.UseExceptionHandler("/Home/Error");// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.app.UseHsts();
    }app.UseHttpsRedirection();
    //app.UseMiddleware<MarkdownRenderingMiddleware>();
    app.UseMarkdownRendering(templatePath: "/template/layout.html",fileProviderRoot:"Content");
    app.UseStaticFiles();app.UseRouting();app.UseAuthorization();app.MapControllerRoute(name: "default",pattern: "{controller=Home}/{action=Index}/{id?}");app.Run();

5)模板文件示例

  1. layout.html
    <!-- Content/template/layout.html -->
    <!DOCTYPE html>
    <html>
    <head><meta charset="utf-8"><title>Markdown Render</title><link rel="stylesheet" href="/styles/markdown.css">
    </head>
    <body><div class="markdown-body">{0} <!-- Markdown 内容插入位置 --></div>
    </body>
    </html>
    

6)*.md文件示例

  1. test.md

7)缓存优化

  1. 在中间件添加内存缓存
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Caching.Memory;namespace MarkDownMiddleware.Middleware
{public class MarkdownRenderingMiddleware{private readonly RequestDelegate next;private readonly IFileProvider _fileProvider;private readonly string _template;private readonly IMemoryCache _memoryCache;public MarkdownRenderingMiddleware(RequestDelegate next,IFileProvider fileProvider,string template = null,IMemoryCache memoryCache = null){this.next = next;_fileProvider = fileProvider;_template = template ?? "<html><body>{0}</body></html>";_memoryCache = memoryCache;}public async Task InvokeAsync(HttpContext context){var path=context.Request.Path.Value;if (!path.EndsWith(".md")&&!path.EndsWith(".markdown")){await next(context);return;}var cacheKey = $"markdown_{path}";if (_memoryCache.TryGetValue(cacheKey, out string cachedHtml)){await context.Response.WriteAsync(cachedHtml);return;}var fileInfo=_fileProvider.GetFileInfo(path);if (!fileInfo.Exists){context.Response.StatusCode = 404;await context.Response.WriteAsync($"Markdown file ({path}) not found");return;}// 读取 Markdown 内容using var stream = fileInfo.CreateReadStream();using var reader = new StreamReader(stream);var markdown = await reader.ReadToEndAsync();// 转换为 HTMLvar html = Markdig.Markdown.ToHtml(markdown);// 嵌入模板var fullHtml = string.Format(_template, html);// 返回响应context.Response.ContentType = "text/html";await context.Response.WriteAsync(fullHtml);_memoryCache.Set(cacheKey, fullHtml, TimeSpan.FromMinutes(10));}}
}

8)使用示例

  1. 访问https://localhost:7066/test.md

三、注意事项

  • 安全性:限制文件目录,避免路径遍历攻击。

  • 性能:对高频访问的 Markdown 文件启用缓存。

  • SEO 优化:在模板中添加 标签增强搜索引擎友好性。


总结

通过此中间件,可快速实现 Markdown 内容的动态渲染,适用于文档系统、博客引擎等场景。

http://www.xdnf.cn/news/336943.html

相关文章:

  • 信创生态核心技术栈:数据库与中间件
  • 《智能网联汽车 自动驾驶功能场地试验方法及要求》 GB/T 41798-2022——解读
  • Mac 平台 字体Unicode范围分析器
  • 使用迁移学习的自动驾驶汽车信息物理系统安全策略
  • MySQL数据库创建、删除、修改
  • Android NDK版本迭代与FFmpeg交叉编译完全指南
  • ubuntu24.04安装anaconda
  • SwiftData 数据持久化解决方案
  • 如何使用极狐GitLab 软件包仓库功能托管 python?
  • git设置tabsize
  • Kubernetes client-go 客户端类型与初始化指南
  • 驱动开发硬核特训 · Day 30(上篇):深入理解 I2C 总线驱动模型(以 at24 EEPROM 为例)
  • Dynamic Causal Modeling在医疗AI领域的编程案例与应用研究
  • 〖 Linux 〗解决 VS Code 远程连接服务器的常见问题
  • iPhone手机连接WiFi异常解决方法
  • 【hadoop】案例:Sqoop迁移仓库数据
  • 5、开放式PLC梯形图编程组件 - /自动化与控制组件/open-plc-programming
  • Lua学习笔记
  • 无刷电机控制算法策略
  • AI驱动的制造工艺:系统化探索与创新
  • 【hadoop】Hbase java api 案例
  • 【嵌入式开发-CAN】
  • 美化IDEA注释:Idea 中快捷键 Ctrl + / 自动注释的缩进(避免添加注释自动到行首)以及 Ctrl + Alt + l 全局格式化代码的注释缩进
  • Java 异常
  • 深入理解 Docker 网络原理:构建高效、灵活的容器网络
  • 缓存局部性保留
  • 【Python】PDF文件处理(PyPDF2、borb、fitz)
  • 2022年8月,​韩先超对中移信息进行微服务架构原理(Docker+k8s+DevOps+Go等)培训
  • MYSQL的行级锁到底锁的是什么东西
  • iOS 模块化开发流程