Flask学习9:在服务器上处理富文本

继上一篇博客(Flask学习8),提交表单后,POST 请求只会发送纯 Markdown 文本,页面中显示的 HTML 预览会被丢掉。和表单一起发送生成的 HTML 预览有安全隐患,因为攻击者轻易就能修改 HTML 代码,让其和 Markdown 源不匹配,然后再提交表单。安全起见,只提交 Markdown 源文本,在服务器上使用 Markdown(使用 Python 编写的 Markdown 到 HTML 转换程序)将其转换成 HTML。得到 HTML 后,再使用 Bleach 进行清理,确保其中只包含几个允许使用的HTML 标签。

把 Markdown 格式的博客文章转换成 HTML 的过程可以在 _posts.html 模板中完成,但这么做效率不高,因为每次渲染页面时都要转换一次。为了避免重复工作,我们可在创建博客文章时做一次性转换。转换后的博客文章 HTML 代码缓存在 Post 模型的一个新字段中,在模板中可以直接调用。文章的 Markdown 源文本还要保存在数据库中,以防需要编辑,修改后的模型如下:

...
from markdown import markdown
import bleach

class Posts(db.Model):
    ...
    content_html = db.Column(db.Text)

    @staticmethod
    def on_changed_content(target, value, oldvalue, initiator):
        allowed_tags = [
                        'a', 'abbr', 'acronym', 'b', 'blockquote', 'code',
                        'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul',
                        'h1', 'h2', 'h3', 'p'
                        ]
        target.content_html = bleach.linkify(bleach.clean(markdown(value, output_format='html'), tags=allowed_tags, strip=True))
db.event.listen(Posts.content, 'set', Posts.on_changed_content)

on_changed_body 函数注册在 body 字段上,是 SQLAlchemy“set”事件的监听程序,这意味着只要这个类实例的 body 字段设了新值,函数就会自动被调用。on_changed_body 函数把 body 字段中的文本渲染成 HTML 格式,结果保存在 body_html 中,自动且高效地完成Markdown 文本到 HTML 的转换。真正的转换过程分三步完成。首先,markdown() 函数初步把 Markdown 文本转换成 HTML。
然后,把得到的结果和允许使用的 HTML 标签列表传给 clean() 函数。clean() 函数删除所有不在白名单中的标签。转换的最后一步由 linkify() 函数完成,这个函数由 Bleach 提供,把纯文本中的 URL 转换成适当的链接。最后一步是很有必要的,因为 Markdown规范没有为自动生成链接提供官方支持。PageDown 以扩展的形式实现了这个功能,因此在服务器上要调用 linkify() 函数。

最后,如果 post.body_html 字段存在,还要把 post.body 换成 post.body_html:

# 文章内容部分显示
<a>
    {% if post.content_html %}
          {{ post.content_html }}
   {% else %}
          {{ post.content }}
   {% endif %}
</a>
标签:Flask 发布于:2019-10-21 11:56:11