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

文章目录


前言

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

一、核心功能

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

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

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

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

二、实现步骤

1)安装依赖包

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

    bash 复制代码
    Install-Package Markdig

2)创建中间件类

  1. MarkdownRenderingMiddleware.cs

    bash 复制代码
    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();
    
                // 转换为 HTML
                var 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

    bash 复制代码
    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

    bash 复制代码
    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

    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. 在中间件添加内存缓存
csharp 复制代码
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();

            // 转换为 HTML
            var 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 内容的动态渲染,适用于文档系统、博客引擎等场景。

相关推荐
gxn_mmf1 小时前
典籍知识问答模块AI问答功能feedbackBug修改+添加对话名称修改功能
前端·后端·bug
向哆哆2 小时前
Spring Boot快速开发:从零开始搭建一个企业级应用
java·spring boot·后端
界面开发小八哥3 小时前
DevExtreme JS & ASP.NET Core v25.1新功能预览 - 全新的Stepper组件
javascript·asp.net·界面控件·devexpress·ui开发·devextreme
eternal__day8 小时前
Spring Boot 实现验证码生成与校验:从零开始构建安全登录系统
java·spring boot·后端·安全·java-ee·学习方法
海天胜景9 小时前
HTTP Error 500.31 - Failed to load ASP.NET Core runtime
后端·asp.net
海天胜景9 小时前
Asp.Net Core IIS发布后PUT、DELETE请求错误405
数据库·后端·asp.net
源码云商11 小时前
Spring Boot + Vue 实现在线视频教育平台
vue.js·spring boot·后端
RunsenLIu13 小时前
基于Django实现的篮球论坛管理系统
后端·python·django
HelloZheQ15 小时前
Go:简洁高效,构建现代应用的利器
开发语言·后端·golang