当前位置: 首页 > 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 内容的动态渲染,适用于文档系统、博客引擎等场景。

相关文章:

  • DRF+Vue项目线上部署:腾讯云+Centos7.6
  • 22、近端策略优化算法(PPO)论文笔记
  • 深入理解栈数据结构(Java实现):从原理到实战应用
  • 什么是延迟队列?RabbitMQ 如何实现延迟队列?
  • Lost connect to debugger on ‘iphone‘
  • [ctfshow web入门] web58
  • 【算法-链表】链表操作技巧:常见算法
  • 《数据结构初阶》【链式二叉树】
  • 从父类到子类:C++ 继承的奇妙旅程(1)
  • 什么是HTML、CSS 和 JavaScript?
  • 如何阅读、学习 Git 核心源代码 ?
  • 使用C# ASP.NET创建一个可以由服务端推送信息至客户端的WEB应用(2)
  • 缓存套餐-03.功能测试
  • 缓存(1):三级缓存
  • 如何利用 Elastic Load Balancing 提升应用性能与可用性?
  • java CyclicBarrier
  • 模拟堆(算法题)
  • Linux电源管理(7)_Wakeup events framework
  • 【神经网络与深度学习】VAE 在解码前进行重参数化
  • 前端线上错误日志收集与定位指南
  • 中华人民共和国和俄罗斯联邦关于进一步加强合作维护国际法权威的联合声明
  • 太原一高中生指出博物馆多件藏品标识不当,馆方已邀请他和专家共同探讨
  • 最新研究:基因编辑治疗晚期胃肠道癌显成效
  • 以色列计划“占领加沙”,特朗普下周中东行结束之际将是“机会窗口”
  • 国铁集团:铁路五一假期旅客发送量累计已超1亿人次
  • 贵州黔西游船发生侧翻事故,游客:事发时能见度只有一米,所乘船只停靠礁石避险