简介:在线协作工具 OnlyOffice 的 Python 连接器二次开发包,面向需要自建局域网在线文档编辑服务、或希望将 OnlyOffice 深度集成到网站后台的开发与运维人员。这一版本在官方开源工程基础上完成了贴近国内使用场景的实用改造:默认字体切换为中文,移除拼写检查导致的红波浪下划线,同时修复了局域网内多人协作无法正常连通的典型问题。压缩包共包含 83 个文件,体积仅 1.68MB,包内核心逻辑集中在 Python 脚本中,前端界面由 SVG、CSS、JS 等静态资源组成,并附带 HTML 模板、多种 Office 示例文档以及配置说明,目录划分明确,便于按模块查看和二次修改。目前已有 1044 人学习/下载。资源还给出经实际安装验证的依赖版本清单,帮助规避不同库之间的版本冲突;针对配置文件中的存储路径与服务地址调整,也提供了可直接套用的思路,可显著缩短 OnlyOffice 从部署到局域网可用的调试周期。
1. 在线协作工具为什么偏偏绕不开 OnlyOffice:Python 连接器的位置在哪
当我们想给网站加一个在线协作工具,OnlyOffice 几乎是绕不开的选型。它自带文档、表格、幻灯片编辑能力,但怎么用 Python 连接器把它接进自己的站点,才是工作量所在。很多人以为集成 OnlyOffice 等于在页面里贴一行 JavaScript,结果联调时反复翻车。
这里说的 Python 连接器,不是某个现成插件,而是你自己写的一层胶水:网站后端生成编辑器配置、提供文档下载地址、处理保存回调。这套东西决定了用户能不能在浏览器里编辑、保存、多人同时操作。适合已经用 Python 写业务后端的团队,比如有 Flask 或 Django 的站点,想让用户直接编辑服务器上的文件。
读完这篇文章,你能拆清楚整个链路,拿到最小可跑的连接器写法,并且避开我踩过的那些坑。文章按“机制到实现、权限到排查、再进阶”的顺序走,新手能跟步骤,熟手可以直接看参数和避坑部分。
2. OnlyOffice 在线协作的机制与集成方式:从文档 URL 到回调的完整链路
要把 OnlyOffice 连进网站,先得建立一个共识:浏览器打开的编辑器界面是由 Document Server 渲染的,不是你的 Python 后端渲染的。你的网站负责“喂数据”,Document Server 负责“干活”。这条链路上有四个关键节点:页面、下载接口、回调接口、存储。下面拆开讲。
2.1 浏览器并不直接读文件:OnlyOffice 加载文档的两种角色
浏览器打开你的站点页面,页面引入 Document Server 的 api.js,然后通过 JavaScript 调用new DocsAPI.DocEditor(...)把编辑器渲染到页面。下面这段是前端页面的核心调用,它会先加载api.js,再把配置对象传进去:
const docElement = document.getElementById("placeholder"); new DocsAPI.DocEditor(docElement, { document: { fileType: "docx", key: "doc-key", title: "需求说明.docx", url: "https://your-site.example/api/onlyoffice/download?doc_id=1" }, editorConfig: { callbackUrl: "https://your-site.example/api/onlyoffice/callback", lang: "zh-CN" } });这里的配置对象,就是你的 Python 连接器在后端生成并返回的。注意,浏览器只负责渲染,真正下载文档内容的是 Document Server,不是浏览器。Document Server 会按照document.url发起 HTTP 请求,把文件字节拉到自己这边。所以很多人把url写成 localhost,页面在浏览器上能打开,但 Document Server 在容器里访问 localhost 指向自己,最终白屏。这是集成 OnlyOffice 最容易栽的第一个坑。
第二个角色是回调。用户编辑过程中或关闭页面时,Document Server 会把最新文件通过 POST 推送到editorConfig.callbackUrl指向的接口。你的连接器收到这个请求后,需要把文件保存到你自己的存储里。下载接口和回调接口,就是连接器必须实现的两个端点。
为什么很多人最终选择用 Python 写连接器,而不是纯前端库?因为 OnlyOffice 的多人协作、在线保存、格式转换都封装在 Document Server 服务里,前端只需要配置和回调。而配置的生成、回调的接收涉及业务权限和存储,天然是后端的事。如果你后端已经是 Python,那用 Python 封装一套连接器是最省事的方案。选型时也要注意:OnlyOffice 对 docx、xlsx、pptx 这类 Office 格式支持最好,如果业务里存的是纯文本或 Markdown,需要先转换,这通常在下载接口里完成。
| 角色 | 由谁承担 | 关键动作 |
|---|---|---|
| 页面 | 浏览器 | 加载 api.js,调用 DocEditor |
| 文档下载 | Document Server → 连接器 | 连接器返回文件字节 |
| 保存回调 | Document Server → 连接器 | 连接器接收新文件并写存储 |
| 业务端 | Python 连接器 | 维护权限、签名、文档版本 |
这张表把链路讲清楚了,下面的服务器端工作就是围绕这些动作展开。
2.2 服务器端要做什么:配置生成、JWT 与回调接口
连接器要做的第一件事,是生成编辑器配置。每次用户打开文档,后端要根据文档 ID、当前用户、权限设置生成一份唯一的配置对象。配置对象里内容主要分为三块:document描述文档来源和权限,editorConfig描述编辑器行为和回调地址,token作为完整性签名。
editor_config = { "document": { "fileType": "docx", "key": "document-key-001", "title": "需求说明.docx", "url": "https://your-site.example/api/onlyoffice/download?doc_id=001", "permissions": {"edit": True} }, "editorConfig": { "callbackUrl": "https://your-site.example/api/onlyoffice/callback", "user": {"id": "u_1001", "name": "A同学"} } }这个字典里的url和callbackUrl是关键,它最终要被 Document Server 使用。第二件事是签名。如果 Document Server 启用了安全密钥,你必须在配置里附带token,否则请求会被拒绝。签名工具一般用 JWT,常见做法是把整个配置对象作为payload包一层:
import jwt, time def sign_config(config: dict, secret: str, expire_seconds: int = 600) -> str: payload = {"payload": config, "exp": int(time.time()) + expire_seconds} return jwt.encode(payload, secret, algorithm="HS256")这里有几个参数要说明:exp是过期时间,建议 600 到 3600 秒之间,太短会导致加载慢时 401,太长不安全;payload第一层字段名必须是payload,这是 OnlyOffice 默认的解析位置,不同版本可能有差异,但按这个写最稳。签名之后把生成的字符串塞回editor_config["token"]。
第三件事是回调处理。OnlyOffice 保存文档时会 POST 一个 JSON,常见结构里有status、url、key、users等字段。你要根据状态码决定做什么:
| status | 含义 | 连接器该做什么 |
|---|---|---|
| 1 | 用户正在编辑 | 可选,记录在线状态 |
| 2 | 保存完成 | 从url下载文件并覆盖存储 |
| 3 | 保存出错 | 记录日志并通知维护人员 |
| 4 | 用户关闭文档 | 清理在线状态 |
| 6 | 保存时内容为空 | 通常需要忽略,避免覆盖原文件 |
回调接口除了处理状态,还要验证签名。OnlyOffice 会在回调 body 里带 token,也可能在Authorization头里带。校验失败时,接口返回 JSON 中的error非 0,OnlyOffice 会认为保存失败并稍后重试。这一点对最终一致性很重要,连接器必须实现幂等保存:同一个doc_id即使收到多次status=2,也不能把文件写坏。
到这里,原理和选型都清楚了。接下来说具体怎么用 Python 实现一个最小连接器。
3. 用 Python 写一个可复用的 OnlyOffice 连接器:目录设计与最小实现
这一章直接进入代码。我建议把连接器放在一个独立目录中,让它和业务代码解耦。这样后续测试、替换 OnlyOffice 版本、做多租户扩展都更方便。
3.1 连接器骨架:配置对象与签名工具
目录结构如下:
onlyoffice_connector/ __init__.py settings.py signer.py builder.py callback.pysettings.py负责集中读取环境配置,避免在业务代码里到处写死 Document Server 地址和密钥。最小实现长这样:
from dataclasses import dataclass @dataclass class OnlyOfficeSettings: doc_service_url: str # Document Server 地址,例如 http://docserver.example.com secret: str # JWT 签名密钥,与 Document Server 安全设置保持一致 token_ttl: int = 600 # token 有效期,单位:秒 download_prefix: str = "https://your-site.example/api/onlyoffice/download" callback_prefix: str = "https://your-site.example/api/onlyoffice/callback"把这些参数集中在 dataclass 里,能减少出错。doc_service_url不仅用于前端加载 api.js,后端也可以根据它拼接文档下载地址。secret必须放在环境变量或密钥管理服务里,不能提交到代码仓库。token_ttl默认 10 分钟,如果编辑器加载后第一次下载就 401,优先看这个值。
signer.py是连接器的安全基础,提供签名和验签两个函数:
import jwt import time def sign_token(settings: OnlyOfficeSettings, payload: dict) -> str: token_payload = {"payload": payload, "exp": int(time.time()) + settings.token_ttl} return jwt.encode(token_payload, settings.secret, algorithm="HS256") def verify_token(settings: OnlyOfficeSettings, token: str) -> dict: data = jwt.decode(token, settings.secret, algorithms=["HS256"]) return data.get("payload", data)jwt.encode返回的是字符串,直接放进配置的token字段。过期时间由exp控制,jwt.decode会自动校验,过期会抛出ExpiredSignatureError,所以调用方要捕获异常。这里要注意,verify 返回值里拿到的payload可能是完整配置对象,也可能是你自己在下载 URL 里签的简单字段,所以用data.get("payload", data)做了兼容。
3.2 生成编辑器配置:支持自定义权限与语言
builder.py负责组装配置对象。这个函数会被业务代码反复调用,需要把用户、文档、权限、语言都作为参数传进来:
from urllib.parse import quote def build_editor_config( settings: OnlyOfficeSettings, doc_id: str, title: str, file_type: str, user: dict, permissions: dict | None = None, lang: str = "zh-CN", ) -> dict: doc_key = f"doc_{doc_id}" # 内容变更时必须重新生成 key download_url = f"{settings.download_prefix}?doc_id={quote(doc_id)}" callback_url = f"{settings.callback_prefix}?doc_id={quote(doc_id)}" config = { "document": { "fileType": file_type, "key": doc_key, "title": title, "url": download_url, "permissions": permissions or { "edit": True, "comment": True, "download": True, "print": True, }, }, "editorConfig": { "callbackUrl": callback_url, "user": {"id": user["id"], "name": user["name"]}, "lang": lang, "mode": "edit" if permissions["edit"] else "view", }, } config["token"] = sign_token(settings, config) return config逻辑说明:doc_key是 OnlyOffice 的缓存标识,同一个 key 代表同一份文档。如果文件内容在外部被替换,key 必须变化,否则 Document Server 会认为还是旧文档而不重新拉取。所以实际业务中,文档每次有新版本都要在外部生成新的 key。示例里只用了 doc_id,生产环境建议拼上内容版本号或更新时间戳,比如doc_{doc_id}_{version}。
注意:
doc_key必须全局唯一,建议用文档内容哈希或更新时间戳作为版本段,避免多人打开同一文档时命中共用的旧缓存。
file_type参数不要带点,传docx而不是.docx。user字典必须包含id和name,id用数据库主键的字符串形式。permissions如果传了edit: False,mode会自动变成view,编辑器进入只读模式。lang传zh-CN时工具栏是中文。download_url和callback_url都用quote编码doc_id,因为业务里的文档 ID 可能是 UUID,也可能是带斜杠的路径。
3.3 处理保存回调:把在线文档写回你自己的存储
callback.py是最能体现连接器价值的部分。你需要处理 OnlyOffice 的回调请求,把最新文件写入自己的存储系统。这里用一个抽象的storage对象来接存储层:
import requests def handle_callback( settings: OnlyOfficeSettings, body: dict, headers: dict, storage, ) -> dict: token = body.get("token") or headers.get("Authorization", "").replace("Bearer ", "") payload = verify_token(settings, token) if token else {} if "token" in body and not payload: return {"error": 401} status = body.get("status") doc_id = body.get("doc_id") if status == 2 and body.get("url"): resp = requests.get(body["url"], timeout=60) resp.raise_for_status() storage.save(doc_id, resp.content) return {"error": 0}逻辑说明:先取 token,优先看 body 里的token字段,再看Authorization头。OnlyOffice 两种方式都可能用。验签失败就返回{"error": 401},OnlyOffice 收到后会停止本次保存并记录错误。当status==2时,说明 Document Server 已经生成了最新文件,body["url"]是一个临时下载地址,必须立刻下载。文件下载后交给storage.save持久化。
这个函数的参数值得细说:timeout=60是为了防止下载大文件时连接被断开,如果你的文档经常超过 50MB,建议改成分块下载并在后台任务里执行;resp.raise_for_status()会把 4xx/5xx 变成异常,调用方应该捕获并返回{"error": 1},让 OnlyOffice 重试。storage接口由你的业务层实现,本地文件、数据库、对象存储都可以。这里要特别注意:回调处理必须是幂等的,同一个doc_id连续收到两次相同内容,第二次也不能抛错,否则 OnlyOffice 的重试机制会加大负载。
到这里,一个最小可用的连接器就完整了。下一章讲身份认证和权限控制,这是多用户协作时最容易出问题的部分。
4. OnlyOffice 连接器的身份认证与权限控制:多用户协作时的关键设置
多人同时编辑同一个文档,如果没有处理好身份和权限,会出现光标串号、可编辑区域失控、文件被越权覆盖等事故。这一章把连接器里的用户模型和权限模型说清楚。
4.1 用户身份注入:让编辑器知道谁在打字
OnlyOffice 在多人协作时,靠editorConfig.user里的id区分用户。它决定你的光标颜色、在线列表中的显示名、评论归属。如果连接器没有正确传 user,所有人都会变成同一个匿名用户,互相干扰。
常见做法是每次打开文档的请求里,从当前登录态取出用户,拼到配置里。下面是一个 Django/Flask 风格的示意:
def get_current_user(request): # 从 session 或认证中间件里取 return { "id": str(request.session.get("user_id")), "name": request.session.get("display_name", "A同学") } def open_doc(request, doc_id): user = get_current_user(request) config = build_editor_config( settings, doc_id, title="需求说明.docx", file_type="docx", user=user, permissions={"edit": True, "comment": True, "download": False, "print": True}, lang="zh-CN", ) return {"editor_config": config}参数说明:user.id必须用持久化的用户主键,不要用昵称或邮箱,因为昵称可以重复,邮箱可能在不同身份下相同。name是展示名,可以是中文。open_doc每次调用都重新构建配置,千万不要把配置缓存到全局变量里。同一个用户第二次打开文档,权限可能已经变化,缓存配置会导致越权或编辑状态错误。
如果业务里存在“游客预览”场景,也要给游客生成一个临时id,比如guest_{uuid},并把permissions.edit设为 false。这样 OnlyOffice 会把每个游客当作独立身份,但都处于只读态。
4.2 权限边界:可编辑、可评论、只读与表单模式
document.permissions里的布尔值控制具体能力。不同角色需要不同的权限组合,建议在连接器里维护一个按角色取权限的函数,而不是到处写死 true/false。
| 使用场景 | edit | comment | download | editorConfig.mode | |
|---|---|---|---|---|---|
| 项目成员编辑 | true | true | true | true | edit |
| 访客只读 | false | false | false | true | view |
| 评审人评论 | false | true | false | true | edit |
| 合同归档 | false | false | false | false | view |
当edit=false且comment=true时,mode可以仍然用edit,用户能插入评论但改不了正文。只读模式通常配合mode=view,工具栏会隐藏编辑项。这里有一个容易混淆的地方:表单填写不是只读,也不是完全编辑。OnlyOffice 对表单文档有独立的模式,路由到连接器时要单独判断。如果只是简单地把permissions.edit改成 false,用户可能既不能填也不能改。所以遇到表单文档,要单独走一套配置模板,我一般会在文档类型字段里加一个forms: true的标记,然后让连接器生成不同的权限组合。
权限参数还要与下载接口联动。download为 false 时,文档在 OnlyOffice 里不允许导出,但你的下载接口如果放开,用户仍然可以绕过编辑器直接拉原文件。所以连接器生成下载 URL 时,要把权限信息签进 token,下载接口验签后再校验用户权限。
4.3 防止越权:回调与下载地址的 token 校验
下载接口是安全防线。Document Server 会带着连接器给的 URL 来请求文件,如果 URL 里没有签名,任何人拿到这个地址都能下载。常见做法是在下载 URL 里加一个token查询参数,由连接器在build_editor_config里对文档 ID 和权限签名。
def make_download_url(settings, doc_id, user, can_download): payload = { "doc_id": doc_id, "uid": user["id"], "can_download": can_download, "exp": int(time.time()) + 600, } token = sign_token(settings, payload) return f"{settings.download_prefix}?doc_id={quote(doc_id)}&token={quote(token)}"下载接口收到请求时,先验签,再判断can_download是否为 true,最后还要走一遍业务权限校验。为什么验签后还要走业务权限?因为签名只能证明这个 URL 是连接器生成的,不能证明当前请求者的身份。Document Server 发起下载时没有用户会话,所以要在 token 里写入用户 ID 和操作权限,下载接口根据 token 里的用户 ID 重新查库,防止签名泄露后被重放。
回调接口的 token 校验同样重要。OnlyOffice 回调支持在 body 里带 token,也可以在请求头带 Authorization。连接器里做两层解析:
def _extract_token(body, headers): if body.get("token"): return body["token"] auth = headers.get("Authorization", "") return auth[7:] if auth.startswith("Bearer ") else ""解析出 token 后调用verify_token,如果解不出正确 payload,直接返回错误 JSON。OnlyOffice 收到非 0 的error后会按自己的重试策略再次推送,所以连接器要有日志,避免被重试刷屏。这里有一个经验:回调里保存文件时,最好先写临时文件再 rename 到正式位置,防止 Document Server 重试期间读到半截文件。
5. 接入网站后必踩的 5 个坑:OnlyOffice 与 Python 集成的常见问题排查
这一章全部来自真实联调的血泪经验。每一条都按“现象、原因、解决”写,遇到类似问题直接对照。
5.1 文档加载不出来:先是白屏,然后才看到报错
现象:编辑器区域一直转圈,或者白屏,控制台报 “Error during document loading” 或 “The document cannot be generated”。
原因:Document Server 无法访问连接器给的document.url。最常见的是开发机把文档地址写成了http://localhost:8000/...,浏览器能访问,但 Document Server 在自己容器里请求 localhost 指向它自己。另一个原因是下载接口返回了 HTML 错误页,而不是文件字节。
解决:先从 Document Server 内部测试下载地址。用容器执行 curl:
docker exec <docserver容器名> curl -s -o /dev/null -w "%{http_code} %{content_type}\n" "http://你的网站/api/onlyoffice/download?doc_id=1&token=..."如果返回 404 或 405,说明路由没有暴露给 Document Server;如果返回text/html,说明接口逻辑有异常,被框架的错误页接管。下载接口正确的响应头应该类似Content-Type: application/octet-stream。另外,文档 URL 的有效期要足够长,不能把签名过期时间设成 10 秒,Document Server 从拿到配置到发起下载之间有网络延迟。
5.2 回调地址是内网时,保存后文件没变
现象:用户明明点了保存,界面提示已保存,但回到列表页下载文件,还是旧版本。服务端日志里看不到任何回调请求。
原因:callbackUrl填的是http://localhost:8080/callback或类似内网地址,Document Server 把回调请求打到了自己所在的容器网络,根本没到你的 Python 服务。
解决:把回调地址改成 Document Server 可达的地址。如果是 Docker Compose 部署,你的 Python 后端和 Document Server 在同一个自定义网络时,可以用服务名作为主机名,例如http://python-backend:8000/onlyoffice/callback。如果不在同一个网络,就填宿主机的局域网 IP,并在 Python 服务里监听0.0.0.0。改完之后,在网站日志里 grep 一下callback,确认收到 POST 请求。这里要注意,Document Server 的回调地址是它自己发起请求,跟用户浏览器无关,所以不能用“用户能访问的公网地址”来想当然。
5.3 中文文件名变成了乱码
现象:OnlyOffice 编辑器标题显示%E6%B5%8B%E8%AF%95.docx,或者打开文档后另存为时文件名乱码。
原因:生成配置时把title直接放了 URL 编码后的字符串,或者对document.url里的文件名部分做了整体 quote。OnlyOffice 期望title是普通可读文本,URL 里的查询参数才需要编码。
解决:title保持原始中文,不调用 quote;下载接口的参数统一用urlencode编码。代码示例:
from urllib.parse import quote, urlencode title = "需求说明.docx" download_url = "{}?{}".format( settings.download_prefix, urlencode({"doc_id": doc_id, "filename": "需求说明.docx"}) )下载接口收到参数后,用unquote还原,再写入Content-Disposition的filename*。这个坑非常像玄学,因为在浏览器地址栏里点下载地址明明是通的,但 Document Server 拿到的 URL 是另一种编码,版本不同表现还不同。唯一的稳妥方案是:标题字段永远用明文,参数用标准编码。
5.4 多用户同时编辑时身份串号
现象:两个用户同时打开同一文档,看到同一个人的光标,编辑记录和评论全部算到一个人头上,甚至出现一个人被另一个人的操作踢下线。
原因:连接器把user.id写死成固定值,或者把配置对象缓存在服务端全局变量里。Django 和 Flask 的多线程环境下,类属性或模块级变量是共享的,第一个用户的身份会被后续请求覆盖。
解决:确保build_editor_config是纯函数,用户从请求上下文里取,不要用模块级对象缓存配置。排查时,在配置里临时打印user:
# 临时调试:在 builder.py 里输出 logger.info("build config for user: %s", user)然后开两个浏览器无痕窗口分别登录,看日志里两个请求的id是否不同。如果相同,八成是从全局缓存里读了用户。把user作为参数传递后,这个问题会立刻消失。另一个隐蔽来源是浏览器端缓存了上一次的配置,前端要用后端每次返回的新配置重新调用DocEditor,不能复用旧配置对象。
5.5 浏览器控制台一片 401,其实是 JWT 过期时间不对
现象:打开编辑器时,网络请求里出现大量 HTTP 401,OnlyOffice 界面弹 “Invalid token” 或直接空白。
原因:连接器签发 token 的过期时间太短,Document Server 加载编辑器到第一次下载文件之间发生延迟,导致 token 过期;或者连接器与 Document Server 配置的 secret 不一致,也可能是服务器系统时间偏差太大。
解决:先统一 secret。检查 Document Server 配置里的安全密钥,和OnlyOfficeSettings.secret完全一致,注意前后空格。然后把token_ttl调到至少 600 秒。如果时间偏差超过 120 秒,JWT 的exp判断会受时区影响,用date -u检查两台机器时间。最后,可以用一条命令验证签名:
python - <<'EOF' import jwt, time payload = {"payload": {"test": True}, "exp": int(time.time()) + 600} tok = jwt.encode(payload, "secret", algorithm="HS256") print(jwt.decode(tok, "secret", algorithms=["HS256"])) EOF这段脚本能确认你的 JWT 库版本与签名算法没跑偏。如果单独验证没问题,但 Document Server 仍报错,看一下你是否在 Authorization 头里带了Bearer前缀,不同版本对前缀的处理不同。
6. 把连接器跑起来后的验证与进阶:从单机联调到多节点部署
这一章落一个具体技巧:用自检脚本闭环验证连接器,再谈如何演进成独立服务。我自己的经验是,没有自检就去接 OnlyOffice,后期升级一次密钥或域名就全线翻车,所以验证这一步不要省。
6.1 用容器本地起一个 OnlyOffice 环境
本地联调最快的方式是拉一个 Document Server 容器,然后让连接器指向它。常用命令:
docker run -d --name docserver -p 8080:80 onlyoffice/documentserver启动后,把doc_service_url设为http://localhost:8080,下载和回调地址设为你的 Python 服务。注意,回调地址不要用 localhost,否则 Document Server 回调不到你的进程。如果 Python 服务跑在宿主机,用局域网 IP;如果两个服务都在同一 Docker 网络,直接用服务名。联调通过的标准很简单:打开页面能编辑,保存后存储目录里的文件内容有变化。
6.2 健康自检脚本确认闭环
我习惯把下载和回调链路做成一条自动检查,放到发布流程里。核心逻辑是先构建配置,再拉下载文件,最后模拟回调:
def check_once(settings, storage): config = build_editor_config( settings, "check_doc", "自检.docx", "docx", {"id": "u1", "name": "A同学"}, permissions={"edit": True, "comment": True, "download": True, "print": True}, ) assert requests.get(config["document"]["url"]).status_code == 200 fake_body = {"status": 2, "url": "http://your-site.example/tmp/check.docx", "doc_id": "check_doc"} resp = requests.post(config["editorConfig"]["callbackUrl"], json=fake_body) assert resp.json()["error"] == 0这段代码会同时覆盖:签名是否正确、下载接口是否通、回调接口是否认账。注意模拟回调时fake_body里的url需要是一个你的自检环境能访问到的临时文件地址,否则handle_callback里的下载步骤会失败。
6.3 进阶:把连接器做成独立签名服务
当多个业务系统都要接 OnlyOffice,把连接器塞进每个项目会大量重复,而且密钥分散。常见做法是把它独立成一个 HTTP 服务,只暴露两个接口:/build负责生成配置,/callback负责接收保存回调。业务系统带着文档 ID、用户、权限来调用/build,拿到配置后交给前端。这样 Document Server 的密钥只保存在一个地方,升级 OnlyOffice 时只改这一个服务。
我在自己的项目里就是这么收尾的。最初把配置生成函数直接写进 Django 视图,后来加第二个接入方时立刻体会到后悔药不够吃,抽成独立服务之后,新增接入方只需要调一个接口。这个方向特别适合团队里同时有多个技术栈的站点。代码层面没有新东西,就是把这套连接器封装成 FastAPI 路由,把build_editor_config和handle_callback暴露出去,其余保持不变。
最后说一句经验:OnlyOffice 连接器不是写完就能一直跑的,密钥轮换、域名变更、版本升级都会踩到同样的坑。把自检脚本加进 CI,把密钥集中管理,每次升级前跑一遍验证,能省掉大部分夜里被叫醒的麻烦。希望帮到你。
本文还有配套的精品资源,点击获取