flask本身不处理markdown渲染,需手动集成解析器;推荐用markdown库服务端渲染,显式启用tables、fenced_code等扩展,配合safe_mode或|safe过滤器保障安全,并可通过自定义模板过滤器简化调用。

Flask 本身不处理 Markdown 渲染,必须手动集成解析器;直接用 markdown 库最轻量、最可控,misaka 或 markdown2 属于冗余选择。
用 markdown 库做服务端渲染(推荐)
这是最常见也最稳妥的做法:读取 .md 文件或数据库字段,用 markdown.markdown() 转成 HTML,再传给模板。关键点在于安全与扩展性:
-
markdown默认不启用表格、代码高亮、脚注等扩展,需显式传入extensions参数,比如['tables', 'fenced_code', 'codehilite'] - 必须加
safe_mode='escape'(旧版)或改用md = markdown.Markdown(..., output_format='html')+ 模板中用{{ content|safe }},否则用户输入的原始 HTML 会执行 - 如果内容含相对图片路径(如
),要确保静态目录结构匹配,或预处理替换为/static/images/foo.png
在 Jinja2 模板里自动渲染 Markdown 字段
避免每个视图都调用 markdown.markdown(),可注册自定义过滤器:
@app.template_filter('md')
def markdown_filter(text):
import markdown
return markdown.markdown(text, extensions=['tables', 'fenced_code'])
模板中直接写 {{ post.body|md|safe }}。注意两点:
- 过滤器函数里不要重复 import —— 放在模块顶层更高效
- 若
post.body可能为None,得先判空,否则markdown.markdown(None)报AttributeError
前端实时预览时别用 marked.js 直接渲染用户输入
开发带编辑器的博客后台时,常加「实时预览」功能。这时容易犯错:
- 用
marked.parse()渲染未清洗的用户输入,可能执行内联<script></script>—— 必须搭配DOMPurify.sanitize()过滤输出 - 前后端渲染结果不一致:比如 Flask 用
codehilite,而marked不支持,导致预览和发布后样式错乱 - 解决方案是「预览走后端」:编辑器提交纯文本到
/api/preview,Flask 返回渲染后的 HTML,前端只负责插入 —— 避免双端逻辑分裂
Markdown 文件热加载与缓存权衡
如果博客文章存为 content/posts/hello.md,开发时希望改完文件立即刷新看到效果:
- 用
os.path.getmtime()检查文件修改时间,配合functools.lru_cache做简单缓存,但需设maxsize=None并在每次请求前比对时间戳 - 生产环境务必关掉热加载,改用构建时预渲染为 HTML 文件,或加 Redis 缓存渲染结果,否则每个请求都读磁盘+解析 Markdown,QPS 上不去
- 别用
watchdog类库监听文件变化来 reload —— Flask 自带的debug=True已覆盖该需求,额外监听反而引发竞态
真正麻烦的从来不是「怎么把 # 变成
」,而是扩展语法一致性、HTML 输出安全性、以及前后端渲染链路是否对齐 —— 这三处出问题,轻则样式崩坏,重则 XSS 漏洞。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











