news 2026/9/13 11:45:43

marimo 集成 Web 框架实战:用 FastAPI、Flask、FastHTML 托管与调用笔记本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
marimo 集成 Web 框架实战:用 FastAPI、Flask、FastHTML 托管与调用笔记本

marimo 集成 Web 框架实战:用 FastAPI、Flask、FastHTML 托管与调用笔记本

【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo

本篇基于仓库中 examples/frameworks/README.md 及其下六个可运行的完整示例展开,讲解如何用marimo.create_asgi_app()把 marimo 笔记本挂载进 FastAPI、Flask、FastHTML 等 ASGI/WSGI 框架,覆盖多笔记本目录批量服务、登录鉴权中间件(含 WebSocket 场景的纯 ASGI 中间件写法)、笔记本函数转为 API 端点等实战模式,读者可以按文中步骤在本地复制并运行每一个示例。

一、examples/frameworks 示例集概览

examples/frameworks/README.md 说明了该目录的定位:展示 marimo 与各类 Web/ASGI 框架(FastAPI、Flask、FastHTML 等)的集成方式,并提示每个示例目录内都带有独立的README.md,包含该示例的运行说明。目录下的实际示例包括:

示例目录演示能力
examples/frameworks/fastapi/从目录批量创建多个 marimo 应用并挂载为单个 FastAPI 应用,含登录鉴权、.env加载、日志
examples/frameworks/fastapi-auth/推荐的鉴权模式:纯 ASGI 中间件把用户信息注入scope["user"]/scope["meta"],笔记本内用mo.app_meta().request读取
examples/frameworks/fastapi-endpoint/把笔记本中定义/计算逻辑转化为 FastAPI API 端点,支持覆盖全局变量并取回 cell 输出
examples/frameworks/fastapi-github/从一个 GitHub 仓库动态创建多个 marimo 应用并作为单个 FastAPI 应用服务
examples/frameworks/flask/在 Flask(WSGI)中批量服务 marimo 应用,借助 Starlette 桥接
examples/frameworks/fasthtml/用 FastHTML 从目录批量服务 marimo 应用

所有示例都遵循同一运行方式:文件顶部使用 PEP 723 内联脚本元数据声明依赖(requires-python = ">=3.12"),安装uv后在项目根执行uv run --no-project <目录>/main.py即可自动解析依赖并启动,例如:

# 在仓库根目录执行 uv run --no-project examples/frameworks/fastapi/main.py

其中 FastAPI 版示例(examples/frameworks/fastapi/main.py)声明的依赖为:fastapimarimostarlettejinja2itsdangerouspython-dotenvpython-multipartpasslibpydanticvega-datasets==0.9.0;Flask 版(examples/frameworks/flask/main.py)则是flaskmarimoasgirefpython-dotenvflask-sessionwerkzeugvega-datasets==0.9.0

二、核心 API:create_asgi_app / with_app / build

所有示例的骨架都是同一个三步调用链:

import marimo server = marimo.create_asgi_app() # 1. 创建 ASGI 应用构建器 server = server.with_app(path="/app1", # 2. 逐个挂载笔记本 root="path/to/nb.py") asgi_app = server.build() # 3. 构建为可挂载的 ASGI 应用

该 API 的源码位于 marimo/_server/asgi.py,函数签名与文档字符串给出了全部可配置参数:

参数默认值作用
quietFalse抑制标准输出
include_codeFalse在页面中包含笔记本源码
tokenNone应用鉴权 token,不传则为空 token
skew_protectionFalse启用版本偏斜保护中间件,服务器更新后提示客户端重新加载
session_ttl120会话存活时间(秒)
asset_urlNone自定义静态资源加载地址,支持{version}占位符(如 CDN 地址)
redirect_console_to_browserFalse将 stdout/stderr 重定向到浏览器展示
show_tracebacksFalse异常时弹出可查看完整 traceback 的提示框
html_headNone注入每个笔记本页面<head>的自定义 HTML
execute_opengraph_generatorsFalse执行 opengraph 生成器

文档字符串还注明了一个适用前提:该 ASGI 应用仅服务于处于 Run 模式(app.run())的笔记本,即应用模式而非编辑模式。

从源码结构看,ASGIAppBuilder内部会为每个挂载的笔记本维护一个应用缓存(self._app_cache,按缓存键分发httpwebsocket两类 scope),并在某个挂载应用处理请求失败时回落到主应用(marimo/_server/asgi.py),这解释了为什么示例中可以先build()再整体mount到外层框架。

三、FastAPI:从目录批量挂载多个笔记本

examples/frameworks/fastapi/README.md 说明该示例“从目录以编程方式创建多个 marimo 应用,并作为单个 FastAPI 应用服务”,包含:登录鉴权、多应用目录服务、列出所有应用的首屏页、从.env加载环境变量、基础日志。

核心逻辑见 examples/frameworks/fastapi/main.py:

ui_dir = os.path.join(os.path.dirname(__file__), "..", "..", "ui") # 即 examples/ui/ templates_dir = os.path.join(os.path.dirname(__file__), "templates") server = marimo.create_asgi_app() app_names: list[str] = [] for filename in sorted(os.listdir(ui_dir)): if filename.endswith(".py"): app_name = os.path.splitext(filename)[0] app_path = os.path.join(ui_dir, filename) server = server.with_app(path=f"/{app_name}", root=app_path) app_names.append(app_name)

它遍历examples/ui/目录(仓库中该目录下有slider.pyform.pydataframe.py等 30 多个交互组件示例笔记本)下每个.py文件,以文件名为路由路径挂载。随后:

app = FastAPI() templates = Jinja2Templates(directory=templates_dir) ... app.mount("/", server.build()) app.add_middleware( SessionMiddleware, secret_key=os.getenv("SECRET_KEY", "your-secret-key") ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000, log_level="info")

示例同时实现了完整的登录流程:/login提供表单页(模板在 templates/login.html),post_login校验后写入request.session["username"]auth_middleware拦截除/login外的所有请求,未登录则 302 重定向到登录页;首屏 templates/home.html 接收app_names渲染应用列表。模拟用户库users = {"admin": "password123"}在代码注释中明确标注“生产环境请替换为真实数据库”。

四、推荐鉴权模式:纯 ASGI 中间件 + mo.app_meta().request

examples/frameworks/fastapi-auth/README.md 给出的是“把用户信息传入 marimo 笔记本”的推荐模式,并解释了关键设计决策:

marimo 使用 WebSocket 进行实时通信。Starlette 的BaseHTTPMiddleware只对 HTTP 请求生效,在其中设置的scope["user"]在 WebSocket 连接上不可见。纯 ASGI 中间件则两者都能处理。

实现见 examples/frameworks/fastapi-auth/main.py:AuthMiddleware直接实现async def __call__(self, scope, receive, send),对httpwebsocket两类 scope 统一处理——已登录时在scope["user"]写入{"is_authenticated": True, "username": ...}scope["meta"]写入{"role": "admin"};未登录时对 WebSocket 直接close(code=4003),对 HTTP 请求返回 302 跳/loginPUBLIC_PATHS = {"/login"}中的路径放行。

代码中还有一段关于中间件顺序的重要注释(main.py):Starlette 中最后添加的中间件最外层、最先执行;由于AuthMiddleware依赖SessionMiddleware先填充scope["session"],所以要先add_middleware(AuthMiddleware)(内层)、后add_middleware(SessionMiddleware, secret_key=...)(外层)。

笔记本侧读取方式见 examples/frameworks/fastapi-auth/notebook.py:

@app.cell def _(mo): req = mo.app_meta().request user = req.user if req else None meta = req.meta if req else None mo.md(f""" ## User info from `mo.app_meta().request` - **user**: `{user}` - **username**: `{user['username'] if isinstance(user, dict) else 'N/A'}` - **meta**: `{meta}` """)

即通过mo.app_meta().request拿到外层中间件注入的user/meta,无需修改笔记本本身。运行该示例后打开http://localhost:8000/,用admin/password123登录,cell 即显示注入的认证信息。

五、把笔记本变成 API 端点:函数即接口

examples/frameworks/fastapi-endpoint/README.md 展示的是另一种用法——不渲染页面,而是把 marimo 笔记本当作可导入的模块,从中取函数与 cell 输出,供任意 FastAPI 应用调用。两个能力点:把 notebook 中定义的函数转为端点;覆盖全局变量并取回 cell 输出。

笔记本侧 examples/frameworks/fastapi-endpoint/notebook.py 用@app.function装饰器声明纯函数(addgreetfibonaccistats等),并用@app.cell组织了plot(plot_data)等依赖单元格;文件尾部app.run()使其也能独立运行。服务侧 main.py 直接from notebook import add / greet / plot

@app.get("/add/{a}/{b}") async def add_endpoint(request: Request, a: int, b: int) -> int: from notebook import add return add(a, b) @app.get("/plot") async def plot(request: Request): from notebook import plot data = json.loads(request.query_params.get("data")) # 查询参数覆盖 output, _ = plot.run(plot_data=data) # 覆盖变量并执行 cell buf = io.BytesIO() output.save(buf, format="PNG") # 取回 matplotlib->PIL 输出 buf.seek(0) return StreamingResponse(content=buf, media_type="image/png")

这里有两类调用方式值得注意:

  • 直接调用@app.function定义的普通函数(add(1, 2));
  • 对含依赖关系的 cell 使用plot.run(plot_data=data),传入字典即可覆盖该 cell 的外部变量plot_data,返回值是该 cell 的输出(本例为Image),从而把“查询参数 → 覆盖变量 → 执行 → 取输出 → 序列化为 PNG 流式响应”串成一条完整的 API 链路。

按 README 的说明,运行uv run --no-project examples/frameworks/fastapi-endpoint/main.py后执行curl http://localhost:8000/greet?name=coder即可验证;首页/会返回一个 HTML 页面列出/add/1/2/greet?name=World/plot?data=...三个可点击端点,并显示当前 marimo 与 fastapi 版本。

六、Flask 集成:WSGI 与 ASGI 的桥接

examples/frameworks/flask/README.md 的能力清单与 FastAPI 版一致(登录、目录批量服务、首屏列表、.env、日志)。由于 Flask 是 WSGI 框架而 marimo 的 ASGI 应用需要 ASGI 容器,examples/frameworks/flask/main.py 的组装方式值得细看:

marimo_app = marimo.create_asgi_app() for filename in sorted(os.listdir(ui_dir)): # 同样遍历 examples/ui/ if filename.endswith(".py"): marimo_app = marimo_app.with_app(path=f"/{app_name}", root=app_path) app_names.append(app_name) # 把 WSGI 的 Flask 包进 ASGI 容器 wsgi_app = WSGIMiddleware(app) asgi_app = Starlette(routes=[ Route("/", endpoint=lambda request: RedirectResponse(url="/flask/")), Mount("/flask", app=wsgi_app), Mount("/", app=marimo_app.build()), ]) uvicorn.run(asgi_app, host="0.0.0.0", port=8000)

要点:Flask 应用经starlette.middleware.wsgi.WSGIMiddleware包装后与 marimo ASGI 应用一起挂进 Starlette 路由表,最终由uvicorn以 ASGI 方式启动,因此 README 特别注明“这会启动 Flask 开发服务器”(底层其实是 uvicorn 承载)。Flask 侧鉴权采用flask-sessionSESSION_TYPE = 'filesystem')与@login_required装饰器,密码用werkzeug.security.generate_password_hash/check_password_hash加盐哈希,比 FastAPI 示例的明文比对更贴近生产习惯;错误处理由@app.errorhandler(401/404)渲染 templates/error.html。

七、FastHTML 与从 GitHub 仓库服务笔记本

examples/frameworks/fasthtml/README.md 说明该示例同样“从目录以编程方式创建多个 marimo 应用,并作为单个 FastHTML 应用服务”,结构与 FastAPI 版同构(create_asgi_app+with_app循环 +build挂载),入口为 examples/frameworks/fasthtml/main.py。

examples/frameworks/fastapi-github/ 则把笔记本来源从本地目录换成了远程:README 描述其“从 GitHub 仓库以编程方式创建多个 marimo 应用,并作为单个 FastAPI 应用服务”,同样带有应用列表首屏与基础日志;main.py 拉取仓库中的.py文件后再逐一走with_app挂载,适合“笔记本即内容”、随仓库更新即随站点更新的发布场景,页面模板位于 templates/home.html。

八、模式小结与复用建议

综合六个示例,marimo 与 Web 框架的集成可以归纳为三种递进模式:

  1. 整站托管(Run 模式应用)create_asgi_app()→ 循环with_app(path, root)build()mount到外层框架任意位置。FastAPI/Flask/FastHTML/GitHub 四个示例都是这一模式,差别只在外层框架与笔记本来源(本地目录 vs 远程仓库)。
  2. 鉴权增强:在 ASGI 层用纯中间件写scope["user"]/scope["meta"],笔记本内通过mo.app_meta().request消费;注意中间件必须同时覆盖httpwebsocket两类 scope,且注意 Starlette 中间件的添加顺序与执行顺序相反。
  3. 函数级复用:不启动页面,直接导入笔记本模块,调用@app.function定义的普通函数,或用cell.run(变量=值)覆盖外部变量执行 cell 并取回输出(如图片、数据),再序列化为任意 API 响应。

所有示例均可用uv run --no-project <main.py>直接运行验证;若需要向社区贡献新的框架示例,参考 examples/frameworks/README.md 的建议,为示例目录补上README.md运行说明并提交即可。

【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 11:44:42

机场出租车调度建模:SimPy仿真与多目标优化实战

简介&#xff1a;本资源是2022年第十二届MathorCup高校数学建模挑战赛D题的完整解题方案&#xff0c;面向数学建模初学者、竞赛备赛学生及指导教师&#xff0c;聚焦弱覆盖区域基站优化这一典型通信建模问题。压缩包共24个文件&#xff0c;含9个Python脚本&#xff08;如kmeans.…

作者头像 李华
网站建设 2026/9/13 11:42:34

模糊小波神经网络在机器人实时威胁评估中的工程实现

简介&#xff1a;本资源是面向智能控制与机器人竞赛领域的工程实践项目&#xff0c;聚焦模糊小波神经网络&#xff08;FWNN&#xff09;在目标威胁评估中的Matlab实现&#xff0c;特别适配RoboMaster等实时对抗类机器人系统的攻击优先级决策需求。资源提供完整可运行的算法框架…

作者头像 李华
网站建设 2026/9/13 11:41:19

SSD1306 OLED驱动开发:STM32工程与I2C时序解析

简介&#xff1a;一份围绕STM32F103C8T6微控制器的OLED显示屏驱动程序资源&#xff0c;面向嵌入式开发、物联网及智能硬件爱好者&#xff0c;帮助解决OLED屏与STM32之间的接口驱动与显示控制问题。资源包共134个文件&#xff0c;包含C源文件与H头文件、Keil工程配置、编译生成的…

作者头像 李华
网站建设 2026/9/13 11:37:41

433M超外差接收+EV1527解码:从原理图到PCB的完整遥控开关设计

简介&#xff1a;433M无线遥控开关模块硬件设计资料&#xff0c;面向电子工程师、嵌入式爱好者与智能家居开发者&#xff0c;解决了从无线收发原理到220V开关执行的整体设计参考需求。资源共91个文件&#xff0c;压缩包约11.19MB&#xff0c;核心为Altium Designer的PCB与原理图…

作者头像 李华