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

python 复制代码
# -*- 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/代码高亮)
class CustomRenderer(HTMLRenderer):
    def block_code(self, code, info=None):
        # 处理 Mermaid 代码块
        if info and info.strip() == 'mermaid':
            return f'<div class="mermaid">{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)
        return f'<pre>{highlighted}</pre>'

# 初始化 Mistune 解析器(启用所有扩展)
markdown_parser = mistune.create_markdown(
    renderer=CustomRenderer(),
    plugins=[
        'table',       # 表格支持
        'task_lists',  # 任务列表支持
        'footnotes'    # 脚注支持
    ],
    escape=False       # 关键配置:禁用字符转义
)

# ---- 路由配置 ----
# 首页 - 编辑器界面
@app.route('/')
def index():
    return render_template('editor.html', title='Markdown 在线编辑器 (支持表格/公式/Mermaid)')

# 解析 Markdown 为 HTML (API)
@app.route('/api/parse', methods=['POST'])
def parse_markdown():
    try:
        data = request.get_json()
        markdown = data.get('markdown', '')
        
        if not 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'])
def save_document():
    try:
        data = request.get_json()
        filename = data.get('filename', '').strip()
        content = data.get('content', '').strip()
        
        if not filename or not content:
            return jsonify({'error': '文件名和内容不能为空'}), 400
        
        # 拼接文件路径
        file_path = DOCS_DIR / f'{filename}.md'
        # 写入文件(UTF-8 编码)
        with open(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'])
def load_document(filename):
    try:
        file_path = DOCS_DIR / f'{filename}.md'
        
        # 检查文件是否存在
        if not file_path.exists():
            return jsonify({'error': '文件不存在'}), 404
        
        # 读取文件内容
        with open(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'])
def list_docs():
    try:
        # 读取目录下所有 .md 文件
        docs = []
        for file in 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>')
def serve_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. 核心功能说明

  1. Markdown 解析

    • 使用 mistune 2 作为核心解析器,兼容 GFM 规范
    • 自定义渲染器支持 Mermaid 代码块(输出 <div class="mermaid">
    • 使用 Pygments 实现代码高亮(兼容 highlight.js 样式)
    • 启用表格、任务列表、脚注等扩展
  2. API 接口

    • /api/parse:POST 请求,解析 Markdown 为 HTML
    • /api/save:POST 请求,保存 Markdown 文档
    • /api/load/<filename>:GET 请求,加载指定文档
    • /api/docs:GET 请求,获取所有文档列表
  3. 前端兼容

    • 静态资源路径、接口地址与原 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. 关键注意事项

  1. 静态资源 :需要将原 Node.js 版本中 public 目录下的 js/mermaid.min.jsjs/highlight.min.jscss/github-dark.min.css 复制到 Python 项目的 public 目录中。
  2. 公式渲染:数学公式渲染仍由前端 KaTeX 处理,服务端仅负责 Markdown 解析,与原逻辑一致。
  3. 编码 :所有文件读写均使用 UTF-8 编码,确保中文正常显示。
  4. 兼容性:接口返回格式与原 Node.js 版本完全一致,前端无需任何修改即可对接。

该实现完整复刻了原 Node.js 版本的所有核心功能,同时符合 Python/Flask 的开发规范,可直接替换原服务端使用。

相关推荐
apcipot_rain2 小时前
python 多进程多线程 学习笔记
笔记·python·学习
Albert Edison8 小时前
【Python】学生管理系统
开发语言·数据库·python
love530love10 小时前
【ComfyUI】解决 ModuleNotFoundError: No module named ‘inference_core_nodes‘ 问题
人工智能·windows·python·comfyui·inference-core
亚亚的学习和分享11 小时前
python基础语法----条件语句
python
Zzz 小生12 小时前
LangChain Streaming-Overview:流式处理使用完全指南
人工智能·python·语言模型·langchain·github
yzx99101313 小时前
Python数据结构入门指南:从基础到实践
开发语言·数据结构·python
百锦再13 小时前
Jenkins 全面精通指南:从入门到脚本大师
运维·后端·python·servlet·django·flask·jenkins
FYKJ_201013 小时前
springboot大学校园论坛管理系统--附源码42669
java·javascript·spring boot·python·spark·django·php
Loo国昌13 小时前
【AI应用开发实战】 03_LangGraph运行时与状态图编排:从直接执行到图编排的演进之路
人工智能·后端·python·自然语言处理·prompt