Django项目中Markdown转HTML后标签未渲染仅显示源码问题
问题场景
你正在开发基于Python、Django、HTML、Markdown技术栈的类维基百科站点encyclopedia,预期访问/wiki/TITLE路径时展示对应标题的百科条目内容,现有条目包含CSS、Django、Git、HTML、Python,每个条目独立存储为.md格式文件。
当前使用Python的markdown库完成Markdown到HTML的转换后,页面直接把HTML标签作为纯文本展示,标签未被浏览器解析,无法加载对应样式,相关视图代码如下:
from django.shortcuts import render import markdown from . import util def index(request): return render(request, "encyclopedia/index.html", { "entries": util.list_entries() }) def entry(request, title): content = util.get_entry(title) if content == None: content = markdown.markdown(f'## {title.capitalize()}\'s page has not been found') content = markdown.markdown(content) return render(request, f"encyclopedia/entry.html", { 'title': title, 'content': content })
实际页面输出效果:
问题原因
Django模板引擎默认开启自动HTML转义机制,所有通过模板变量传入的HTML标签、特殊字符都会被转义为普通文本实体,不会被浏览器解析渲染,这是框架自带的XSS攻击防护逻辑,也是你遇到标签直接明文显示的核心原因。
另外你当前的视图代码还存在两个隐性问题:
- 代码缩进错误,
content = markdown.markdown(content)和return render(...)两行没有缩进在entry函数体内,运行时会直接报错 - 条目不存在时提前调用了一次
markdown.markdown()转换内容,后续又对转换后的HTML重复做一次Markdown转换,会导致输出格式错乱
修复方案
两种方案二选一即可,优先推荐第一种,安全性更高:
- 方案1:模板层给对应变量加安全过滤器(推荐)
不需要额外修改视图逻辑,只需要打开templates/encyclopedia/entry.html模板文件,找到输出content变量的位置,给变量加safe过滤器,告诉Django这个变量的内容是安全的,不需要转义:
记得同步修正视图代码里的缩进和重复转换问题,修正后的视图代码:<!-- 把原来的 {{ content }} 修改为以下写法 --> <div class="entry-body"> <h1>{{ title }}</h1> {{ content|safe }} </div>from django.shortcuts import render import markdown from . import util def index(request): return render(request, "encyclopedia/index.html", { "entries": util.list_entries() }) def entry(request, title): content = util.get_entry(title) if content == None: content = f'## {title.capitalize()}\'s page has not been found' content = markdown.markdown(content) return render(request, "encyclopedia/entry.html", { 'title': title, 'content': content }) - 方案2:视图层标记转换后的内容为安全文本
如果不想修改模板,可以在视图层导入Django的mark_safe工具,把转换完成的HTML内容标记为安全后再传给模板,修改后的视图代码:from django.shortcuts import render from django.utils.safestring import mark_safe import markdown from . import util def index(request): return render(request, "encyclopedia/index.html", { "entries": util.list_entries() }) def entry(request, title): content = util.get_entry(title) if content == None: content = f'## {title.capitalize()}\'s page has not been found' content = mark_safe(markdown.markdown(content)) return render(request, "encyclopedia/entry.html", { 'title': title, 'content': content })
注意:如果后续开放用户提交、编辑百科条目的功能,一定要对用户提交的Markdown内容做标签过滤,避免恶意脚本被注入引发XSS安全问题,不要全局关闭Django的自动转义功能。
内容的提问来源于stack exchange,提问作者Andrea
相关产品推荐
相关产品推荐

