python:Flask 3, mistune 2, 实现在线编辑 Markdown 文档的 Web 服务程序
承上一篇: nodejs: 能在线编辑 Markdown 文档的 Web 服务程序,更多扩展功能
模仿 app2.js, 请用 python编程 : flask 3, mistune 2, 或者其它模块 实现同样功能的Web服务
以下是使用 Flask 3 + Mistune 2 实现与 app2.js 相同功能的 Python Web 服务,包含 Markdown 解析(支持代码高亮、Mermaid、表格、公式)、文档保存/加载/列表查询等核心功能:
1. 先安装依赖
pip install flask==3.0.3 mistune==2.0.5 pip install pygments python-multipart python-dotenv 2. 完整代码(app.py)
# -*- coding: utf-8 -*-""" Markdown 在线编辑器 (支持表格/公式/Mermaid) """import os import json from pathlib import Path import mistune from mistune.renderers import HTMLRenderer from mistune.plugins import table, task_lists, footnotes from pygments import highlight from pygments.lexers import get_lexer_by_name, guess_lexer from pygments.formatters import HtmlFormatter from flask import Flask, render_template, request, jsonify, send_from_directory # 初始化 Flask 应用 app = Flask(__name__, static_url_path='/', static_folder='public',# 静态文件目录(对应前端资源) template_folder='views')# 模板目录(对应editor.html)# 配置 app.config['JSON_AS_ASCII']=False# 支持中文 DOCS_DIR = Path(__file__).parent /'docs'# 文档保存目录 DOCS_DIR.mkdir(exist_ok=True)# 确保目录存在# ---- Markdown 解析配置 ----# 自定义代码高亮渲染器(支持 Mermaid/代码高亮)classCustomRenderer(HTMLRenderer):defblock_code(self, code, info=None):# 处理 Mermaid 代码块if info and info.strip()=='mermaid':returnf'<div>{mistune.escape(code)}</div>'# 处理普通代码块高亮try:# 尝试获取指定语言的 lexer lexer = get_lexer_by_name(info.strip())if info else guess_lexer(code)except:# 自动检测语言 lexer = guess_lexer(code)# 使用 Pygments 高亮代码 formatter = HtmlFormatter( noclasses=False,# 生成带类名的 HTML(配合 highlight.js 样式) cssclass='hljs',# 兼容 highlight.js 样式类 linenos=False# 不显示行号(可根据需求开启)) highlighted = highlight(code, lexer, formatter)returnf'<pre>{highlighted}</pre>'# 初始化 Mistune 解析器(启用所有扩展) markdown_parser = mistune.create_markdown( renderer=CustomRenderer(), plugins=['table',# 表格支持'task_lists',# 任务列表支持'footnotes'# 脚注支持], escape=False# 关键配置:禁用字符转义)# ---- 路由配置 ----# 首页 - 编辑器界面@app.route('/')defindex():return render_template('editor.html', title='Markdown 在线编辑器 (支持表格/公式/Mermaid)')# 解析 Markdown 为 HTML (API)@app.route('/api/parse', methods=['POST'])defparse_markdown():try: data = request.get_json() markdown = data.get('markdown','')ifnot markdown:return jsonify({'error':'Markdown 内容不能为空'}),400# 解析 Markdown 为 HTML html = markdown_parser(markdown)return jsonify({'html': html})except Exception as e:return jsonify({'error':f'解析 Markdown 失败: {str(e)}'}),500# 保存文档 (API)@app.route('/api/save', methods=['POST'])defsave_document():try: data = request.get_json() filename = data.get('filename','').strip() content = data.get('content','').strip()ifnot filename ornot content:return jsonify({'error':'文件名和内容不能为空'}),400# 拼接文件路径 file_path = DOCS_DIR /f'{filename}.md'# 写入文件(UTF-8 编码)withopen(file_path,'w', encoding='utf-8')as f: f.write(content)return jsonify({'success':True,'message':'文件保存成功','filePath':str(file_path)})except Exception as e:return jsonify({'error':f'保存文件失败: {str(e)}'}),500# 加载文档 (API)@app.route('/api/load/<filename>', methods=['GET'])defload_document(filename):try: file_path = DOCS_DIR /f'{filename}.md'# 检查文件是否存在ifnot file_path.exists():return jsonify({'error':'文件不存在'}),404# 读取文件内容withopen(file_path,'r', encoding='utf-8')as f: content = f.read()return jsonify({'success':True,'content': content})except Exception as e:return jsonify({'error':f'加载文件失败: {str(e)}'}),500# 获取文档列表 (API)@app.route('/api/docs', methods=['GET'])deflist_docs():try:# 读取目录下所有 .md 文件 docs =[]forfilein DOCS_DIR.glob('*.md'): docs.append({'name':file.stem,# 不带扩展名的文件名'path':str(file)})return jsonify({'success':True,'docs': docs})except Exception as e:return jsonify({'error':f'获取文档列表失败: {str(e)}'}),500# 静态文件服务(兼容前端资源加载)@app.route('/public/<path:path>')defserve_static(path):return send_from_directory('public', path)# ---- 启动服务器 ----if __name__ =='__main__':# 启动 Flask 开发服务器(生产环境建议用 Gunicorn) app.run( host='127.0.0.1',# 不允许外部访问 port=8000,# 与原 Node.js 端口保持一致 debug=True# 开发模式(生产环境关闭))3. 目录结构说明
需要和原 Node.js 版本保持一致的目录结构,确保前端页面能正常加载:
TREE /F md-editor md-editor ├── app.py # Python 服务端代码 ├── docs/ # 文档保存目录(自动创建) │ └── demo.md ├── public/ # 静态资源目录(前端 JS/CSS) │ ├── js/ │ │ ├── mermaid.min.js │ │ └── highlight.min.js │ └── css/ │ └── github-dark.min.css └── views/ # 模板目录 └── editor.html # 原 Node.js 版本的 editor.ejs(无需修改) 4. 核心功能说明
- Markdown 解析:
- 使用
mistune 2作为核心解析器,兼容 GFM 规范 - 自定义渲染器支持 Mermaid 代码块(输出
<div>) - 使用
Pygments实现代码高亮(兼容 highlight.js 样式) - 启用表格、任务列表、脚注等扩展
- 使用
- API 接口:
/api/parse:POST 请求,解析 Markdown 为 HTML/api/save:POST 请求,保存 Markdown 文档/api/load/<filename>:GET 请求,加载指定文档/api/docs:GET 请求,获取所有文档列表
- 前端兼容:
- 静态资源路径、接口地址与原 Node.js 版本完全一致
- 复用原
editor.html前端页面,只需修改一行
cd public\css
rename editor.ejs editor.html
修改第6行:<title>{{ title }}</title>
5. 运行方式
直接运行
python app.py
生产环境建议用 Gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 app:app
6. 关键注意事项
- 静态资源:需要将原 Node.js 版本中
public目录下的js/mermaid.min.js、js/highlight.min.js、css/github-dark.min.css复制到 Python 项目的public目录中。 - 公式渲染:数学公式渲染仍由前端 KaTeX 处理,服务端仅负责 Markdown 解析,与原逻辑一致。
- 编码:所有文件读写均使用
UTF-8编码,确保中文正常显示。 - 兼容性:接口返回格式与原 Node.js 版本完全一致,前端无需任何修改即可对接。
该实现完整复刻了原 Node.js 版本的所有核心功能,同时符合 Python/Flask 的开发规范,可直接替换原服务端使用。