OpenProject API v3 深度指南:OpenAPI 3.1 规范、HATEOAS 架构与工作包自动化实战
【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openproject
OpenProject 的 API v3 是一套面向通用自动化场景的 HATEOAS(超媒体即应用状态引擎)REST API,其接口规范以 OpenAPI 3.1 格式编写并以多文件形式维护在仓库中。本文以 docs/api/apiv3/README.md 为骨架,结合规范的入口文件、分片结构与运行时聚合脚本,讲解如何获取完整规范、理解 HAL+JSON 响应模型、完成认证授权,并最终通过表单(Form)、过滤器(Filter)等机制对工作包资源完成创建、检索、更新与删除的完整闭环。
API v3 是什么
API v3 是 OpenProject 面向多种使用场景的通用 API。虽然规范仍在持续开发中(官方在 README 中标注Status: under development),但大量原本需要通过界面手动完成的操作——例如管理工作包、项目和用户——都可以通过它自动化。官方在 docs/api/README.md 中明确了兼容性承诺:在稳定版本中会尽可能保持 API v3 的向后兼容;同时 OpenProject 还提供 SCIM、MCP、BCF API v2.1 以及/.well-known/端点,它们与 API v3 共同构成完整的开放能力面。
规范本体遵循 OpenAPI 3.1 Specification 声明了openapi: 3.1.2、标题为OpenProject API V3 (Stable)、版本为"3",并内置了三个公开服务器(Edge QA 实例、Staging 实例与 Community 实例),方便读者直接对社区实例发起请求进行验证。
规范的多文件组织与运行时聚合
分片目录结构
很多 OpenAPI 工具只支持单一文件,而 OpenProject 的规范在仓库中被刻意拆分为大量 YAML 分片,便于维护与并行开发。以 docs/api/apiv3/ 为根,目录组织如下:
- openapi-spec.yml:总入口,声明 openapi 版本、info 介绍、servers,以及全部
paths的$ref引用; - paths/:按端点拆分的路径定义(如 work_packages.yml 定义
GET /api/v3/work_packages的查询参数与响应); - components/schemas/:资源模型定义(如
work_package_model.yml、user_model.yml、collection_model.yml),共一百余个; - components/examples/ 与 components/responses/:示例报文与公共错误响应;
- tags/:按主题归类的说明文档(如 Collections、Forms、Filters、Work Packages),用于补充跨端点的概念性知识;
- example/README.md:一份完整的端到端实战指南;
- client-libraries/README.md:社区客户端库索引。
入口文件中每个路径都以相对$ref指向对应分片,例如/api/v3引用./paths/root.yml,/api/v3/work_packages引用./paths/work_packages.yml。
在任意服务器上获取完整规范
由于分片结构不被所有工具支持,任何 OpenProject 服务器都会在运行时提供聚合后的单一文件,可直接访问:
GET /api/v3/spec.jsonGET /api/v3/spec.yml
仓库还附带一个脚本,可以本地输出完整规范,格式由--format参数决定(yaml或json,默认json):
./script/api/spec --format yaml > openproject-oas.yml该脚本本身(script/api/spec)非常轻量:它基于 lib/api/open_api.rb 中的API::OpenAPI.assemble_spec读取docs/api/apiv3/openapi-spec.yml,并通过substitute_refs递归替换所有$ref引用,最终输出自包含的规范文档;脚本还通过rescue Errno::EPIPE优雅处理管道被提前关闭(例如head截断输出)的场景。
HAL+JSON 与 HATEOAS 设计
API v3 是一个超媒体 REST API。官方文档明确指出:每个端点返回的响应体中都会携带指向其他资源或操作的链接,而这些链接是上下文敏感的——只有当前认证用户真正具备权限执行的操作才会被渲染出来。例如,通过工作包端点获取一个工作包时,只有当认证用户在该工作包所属项目中被授予了更新权限,响应中才会出现update链接。客户端可以据此动态识别当前用户可执行的动作,这正是 HATEOAS 的核心价值。
在报文格式上,API v3 实现了 HAL+JSON 并扩展了三个元属性:
_type:资源类型标识(如WorkPackage、Project);_links:该资源相关的资源与动作链接集合;_embedded:所有内嵌对象。
值得注意的是,HAL 标准本身并不保证内嵌资源的完整性,但 OpenProject API v3 做出更强承诺:只要资源被内嵌,就一定是完整表示(包含全部属性),而不是部分省略。所有 API 响应(包括集合)都是一个单一的 HAL+JSON 对象,集合成员通过内嵌属性承载。
认证与授权
四种认证方式
OpenProject API v3 支持以下认证方案(详见 openapi-spec.yml 的 info 介绍):
- 会话认证(Session-based):通过 Web 界面登录后在浏览器同源环境下使用,适合内置的 Angular 客户端;出于安全考虑,仅当通过
Sec-Fetch-Site请求头确认请求来自同源时才允许。 - API Token 作为 Bearer Token:在个人账户页生成 API Token(形如
opapi-2519132cdf62dcf5a66fd96394672079f9e9cad1),作为 Bearer 头传递:
API_KEY=opapi-2519132cdf62dcf5a66fd96394672079f9e9cad1 curl -H "Authorization: Bearer $API_KEY" https://community.openproject.org/api/v3/users/42- API Token 通过 Basic Auth:用户名固定为
apikey(注意不是你的登录名),Token 作为密码:
API_KEY=opapi-2519132cdf62dcf5a66fd96394672079f9e9cad1 curl -u apikey:$API_KEY https://community.openproject.org/api/v3/users/42- OAuth 2.0:支持授权码流程(Authorization code flow)、带 PKCE 的授权码流程(推荐给无法安全保管
client_secret的客户端)以及客户端凭证流程(Client credentials,需将应用绑定到某个模拟用户)。使用时需先在管理后台注册 OAuth 应用以获取client_id与client_secret。此外还支持外部授权服务器签发的 JWT(RFC 9068):要求 OIDC 提供方配置了jwks_uri、JWT 使用 RSA 签名、iss与提供方issuer一致、aud包含客户端 ID、scope包含如api_v3的有效 scope,且sub对应的用户已通过登录等方式关联到 OpenProject。
关于“为什么不用用户名+密码做 Basic Auth”,官方给出的理由很有参考价值:API Key 一旦在客户端泄露只需重新生成,不必连带修改密码;天然长且随机,难以被字典攻击破解;更重要的是,通过 OpenID Connect 注册的用户可能根本没有密码。
默认情况下实例可以允许匿名访问(此时按匿名用户权限处理),而要求认证的实例在未认证请求时会返回HTTP 401。
授权:成员关系与 403
认证只解决“你是谁”,授权才决定“你能做什么”。在 OpenProject 中,权限主要通过“用户 + 项目 + 角色”三元组构成的成员关系(Membership)授予:需要为每个用户在其可访问的项目中分配角色(见 example/README.md 的 Authorization 章节)。当权限不足时,API 返回HTTP 403,遇到此类错误应首先检查成员角色配置。
CORS 与压缩、HTTP 方法
默认情况下 API不返回任何 CORS 头;如需允许跨域 AJAX 调用,需要在管理设置中按 API 设置文档选择性开启。响应支持 gzip 与 deflate 压缩,由客户端的Accept-Encoding请求头决定,未发送该头时按identity处理(即不压缩)。允许的 HTTP 方法为:GET(获取单个资源或集合)、POST(创建资源或执行动作)、PATCH(更新资源)、DELETE(删除资源)。
集合(Collections)与分页
集合是 API v3 中最常见的响应形态。官方在 tags/collections.yml 中说明:当端点可能返回多个元素时,API不会直接返回 JSON 数组,而是返回一个特殊的集合对象,元素放在内嵌属性elements中。集合可携带total(元素总数)、pageSize(当前响应包含的元素数)、count(本页实际元素数)、offset(偏移分页时的页码)、groups(聚合分组信息)、totalSums(数值属性聚合)等元信息。
分页有两种方式,取决于具体端点:offset 分页(nextByOffset/previousByOffset/jumpTo)与cursor 分页(nextByCursor/previousByCursor);部分集合不分页或只支持其中一种。HATEOAS 风格的链接包括self(当前页)、changeSize(调整页大小)等模板链接。因此客户端遍历集合时应优先跟随响应中的链接而非手工拼 URL。
表单(Form):创建与更新的安全机制
表单是 API v3 最具特色的设计之一,用于辅助创建或编辑资源。官方 tags/forms.yml 阐述了其三大目标:让资源的可写属性可被发现、展示属性可被设置为何值、在提交前完成校验并反馈错误。向表单端点POST一个空请求体(或空 JSON 对象)即可获得初始表单,后续调用应携带符合表单描述的 JSON 对象。
表单始终内嵌三个属性:
- payload:待提交资源的最新编辑版本,包含全部可写属性并反映最近一次校验的改动,相当于变更预览;即使客户端设置了非法值也会反映在这里,但校验错误会同时指出该 payload 无法提交。注意:修改属性 A 可能影响属性 B 的合法值,若客户端未触碰 B,payload 中会出现默认值并伴随相应校验错误。
- schema:描述底层资源的模式,会随每次重新校验而动态变化(例如切换工作包类型可能改变可用属性与可选值),因此不作为静态链接提供。
- validationErrors:以属性名为键的错误字典,仅包含校验失败的属性;全部通过时为空。
表单提供三个动作链接:validate(校验变更并返回错误与允许值)、commit(仅在表单内容合法时出现,真正执行变更)、previewMarkup(将 markup 渲染为 HTML 预览)。向validate或commit提交时无需包含 payload 中的全部属性,只需带上要修改的属性和lockVersion(若存在)。即使存在校验错误,表单端点也返回 HTTP 200——表单的职责是帮助客户端消除错误,而非报错。
过滤器(Filters)语法与操作符
过滤器可以附加到众多端点,构造精确的数据查询。官方 tags/filters.yml 给出了完整语法:
[ { "<filter name>": { "operator": "<operator>", "values": [<value>, ...] } }, // ... ]例如同时按主题/ID 与状态过滤:
[ { "subjectOrId": { "operator": "**", "values": ["12"] } }, { "status": { "operator": "=", "values": ["5"] } } ]将上述 JSON 字符串化并 URL 编码后通过filters参数附加到端点即可。多个过滤器之间是AND关系,OR 暂不支持。核心操作符速查表如下:
| 符号 | 语义 | values 内容 |
|---|---|---|
= | 等于给定值之一 | 至少一个值 |
&= | 包含全部给定值 | 至少一个值 |
! | 不等于给定值 | 至少一个值 |
>=/<= | 大于等于 / 小于等于 | 单个数值 |
t-/t+ | 过去 / 未来给定天数 | 1 个整数(天) |
<t+/>t+ | 未来少于 / 多于给定天数 | 1 个整数(天) |
<t-/>t- | 过去少于 / 多于给定天数 | 1 个整数(天) |
*/!* | 非 NULL / 为 NULL | 空 |
** | 在所有字符串属性中搜索 | 单个字符串 |
=d | 在指定日期 | 1 个 ISO8601 日期/时间 |
<>d | 在两个日期之间 | 2 个 ISO8601 日期/时间 |
w/t | 本周 / 今天 | 空 |
~/!~ | 按顺序包含 / 不包含给定词(SQL LIKE) | 至少一个字符串 |
工作包还有专属操作符:o(状态为打开)、c(状态为关闭)、ow(手动排序),以及关系类过滤器(blocks/blocked、children/parent、follows/precedes、duplicates/duplicated、partof/includes、relates、requires/required),取值为关系目标工作包的 ID。布尔过滤器值需写成['t'](真)或['f'](假)。
当过滤器 URL 过长时,API 还提供eprops参数:把包含filters、sortBy、pageSize、offset、columns在内的完整查询属性集打包为 JSON 对象,经 zlib 压缩并 Base64 编码后作为一个参数传递;注意所有需为 JSON 的属性(如filters)会被双重编码。各端点实际可用的过滤器列表见对应路径定义,例如 paths/work_packages.yml 中就列出了assigned_to、author、status、subjectOrId、custom_field、dates_interval等数十个过滤器。
工作包 API 实战:从创建到删除
example/README.md 提供了一份与语言无关的完整实战指南(以 Postman 演示,可迁移到任意语言),其原则适用于整个 API v3。
获取工作包列表
最简形式是对GET /api/v3/work_packages发起请求,返回WorkPackageCollection。社区实例对公众开放,无需认证即可访问:
而本地默认安装通常要求认证,未认证请求会收到 401,并提示客户端选择认证方式。实战中推荐两种:Basic Auth(最常用)与 OAuth 2(理应最常用)。无论哪种机制,客户端总是以某个 OpenProject 用户身份行动——即使是无用户交互的 Client credentials 流程,服务端的一切操作也都以绑定用户的名义执行以落实授权。
Basic Auth 与 API Key
使用 Basic Auth 前,用户需先登录 OpenProject,在“我的账户 → 访问令牌(Access token)”页面,通过 API 行内的“生成/重置”按钮创建 API Key。注意:一个用户同一时刻只能有一个 API Key,重新生成会使旧 Key 失效,务必及时保存。在 Postman 中,选择 Basic Auth 类型后,Username 填apikey、Password 粘贴生成的 Key 即可,Postman 会自动设置正确的Authorization头(等价于手工对字符串apikey:[key]做 Base64 编码并前缀Basic)。用户的登录名在此流程中从不被使用。
OAuth 2 流程则需管理员先在后台注册并配置 OAuth 应用,随后用 Postman 的 OAuth 2 流程获取 Bearer Token 并点击 “Use token” 使用。需注意OAuth Token 两小时后过期,届时需要点击 “Get New Access Token” 重新获取。
授权与 403
即使认证成功,返回的集合仍可能为空——因为该用户缺少权限。需要在项目成员管理页为用户分配角色,权限不足时端点返回 403。
创建:表单先行
创建工作包前强烈建议先获取工作包表单(对POST /api/v3/work_packages/form发送空请求体,并设置Content-Type: application/json——所有状态变更请求即 POST/PATCH/DELETE 都必须携带该头)。空表单会在_embedded.validationErrors中列出type、project、subject缺失的错误。内嵌的schema进一步指导客户端:
subject是标量属性,接受任意字符串(最长 255 字符),schema 中不提供可选值;type的可用值直接列在 schema 中(实例中类型数量有限);project只提供一个链接,客户端需调用该链接获取可创建工作包的项目(数量可能成百上千,故不内嵌)。
构造请求体时需区分两类属性:引用资源的属性(project、type)放在_links段并提供href(其值恒为资源的self链接);标量属性(subject)放在根级:
{ // 标量值 "subject": "abc", "_links": { // 资源值 "project": { "href": "some/url" } } }注意project与type的组合必须有效(某些类型在部分项目中不可用);有时先提交project会让type的availableValues更新,因此先填项目再选类型是常见策略。当表单不再报校验错误时,响应中会出现commit链接——这正是 HATEOAS 的体现:客户端跟随链接即可完成创建。向commit链接发送POST(以payload为请求体),服务端返回完整的工作包资源,其中包含默认值、只读字段与可用动作链接,比客户端提交的内容更丰富。
自定义字段(Custom fields)尤其依赖表单:自定义字段及其取值因实例而异,跨实例复用的客户端无法硬编码;其可用性还取决于工作包所属project与type(可配置为仅对特定类型/项目可见)。schema 会列出全部可用自定义字段,带availableValues的属性须放入_links段,标量型(如 integer、float)自定义字段则放在根级:
{ // 标量值 "customFieldX": 123, "_links": { // 资源值 "customFieldY": { "href": "some/url" } } }对于格式为calculated_value的自定义字段,计算过程可能产生错误:此时其标量值为null,并额外返回一个<字段名>Errors数组,每个错误包含机器可读的code(如ERROR_MATHEMATICAL)和本地化的人读message;无错误时该字段不出现,且一次可能有多个错误。
检索与过滤
创建后可重新检索。直接请求GET /api/v3/work_packages会返回该用户可见的全部工作包(服务端始终限制单次返回数量,总数由集合的total属性给出)。更高效的做法是过滤:
- 项目作用域 URL:
/api/v3/projects/:project_identifier_or_id/work_packages(区别于全局的/api/v3/work_packages); - 属性过滤:
filters=[{"subject": { "operator": "~", "values": ["A new work package"] }}]返回主题包含指定字符串的工作包; - 组合过滤:可同时按类型与优先级过滤(如类型 ID 为 2/3/4且优先级 ID 不为 4)。过滤资源属性时提供的是资源 ID,过滤标量属性时直接提供值。
此外还支持排序(sortBy=[["assignee","asc"],["createdAt","desc"]])、分页大小(pageSize=50)与页码偏移(offset=5)。官方建议:直接在 OpenProject 界面中配置好筛选、排序,然后通过浏览器开发者工具观察界面自身发出的请求——因为 OpenProject 前端本身就是一个 API 客户端,它是学习如何与后端正确通信的最佳范例。
更新:lockVersion 与 PATCH
每个工作包资源的链接中带有更新表单链接。向该链接POST(请求体需包含当前lockVersion)可获取更新表单。lockVersion是防止并发覆盖的关键机制:当一个用户修改工作包后,另一个用户若不感知变更而覆盖,就会产生冲突;更新成功后lockVersion会 +1,后续请求必须携带新值。
更新表单与创建表单结构一致(payload、schema、validationErrors)。需要留意的是,改动type或project可能引起可用值乃至属性可用性的变化:自定义字段可能不适用于新组合、切换项目可能改变可选负责人、项目启用的模块会影响属性(如budget依赖预算模块)、用户权限也会影响可写属性(如version仅对拥有“分配版本”权限的用户可写)。
准备完毕且无校验错误后,发送PATCH请求执行更新。PATCH 是部分更新:只需提交要改的属性,未提交的属性保持不变(显式置空用null,如"dueDate": null)。
删除
对工作包的 URL 直接发送DELETE请求即可删除。由于无需请求体,这次Content-Type: application/json头需要手工设置。
客户端库生态
官方鼓励社区为尽可能多的语言开发客户端库以复用连接层工作(docs/api/apiv3/client-libraries/README.md),当前收录了:JavaScript/TypeScript 的op-client(同时支持 Node.js 与浏览器)、Excel 的OpenProjectExcel(Excel 表格与 OpenProject 双向同步)以及 Go 语言的go-openproject。官方不对所列库背书,但欢迎开发者贡献更多语言实现。
总结
OpenProject API v3 以 OpenAPI 3.1 多文件分片形式维护规范(docs/api/apiv3/openapi-spec.yml),既可以通过任意实例的/api/v3/spec.json、/api/v3/spec.yml或仓库内 script/api/spec 脚本获得自包含规范,也通过 HATEOAS 链接、上下文敏感的可用动作、动态校验的表单机制、标准化的过滤器与集合分页,为工作包、项目、用户等资源的自动化管理提供了统一且可探索的契约。从源码结构看,lib/api/open_api.rb的assemble_spec聚合逻辑与docs/api/apiv3/的分片布局一一对应,读者可沿 paths/、components/schemas/ 与 tags/ 三个维度深入阅读任意资源的完整定义;需要完整走一遍 API 调用流程时,example/README.md 是值得逐屏对照的实操手册。
【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openproject
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考