Inventaire 5.1.1 API 详解
Inventaire 5.1.1 服务端 REST API 逐域实测详解:路由机制与三种认证方式(会话 cookie/HTTP Basic/feeds 读令牌)、items/entities/users/groups/shelves/search/feeds 等各域端点请求与响应回显、统一错误结构、分页约定与常见坑,全部 curl 请求与 JSON 响应逐行照抄实测结果。
Inventaire 5.1.1 API 详解
目标:逐域梳理并实测 Inventaire 5.1.1 服务端 REST API——路由机制、三种认证方式、items / entities / users / groups / shelves / search / feeds 等各域端点的请求形态与真实响应、统一错误结构与分页约定。本文全部请求与响应回显均在 Ubuntu 22.04 服务器实测采集(实例按 《Inventaire 5.1.1 Docker Compose 部署指南》部署,服务监听 8138 端口),实测日期 2026-10-24;文中每一条
curl命令与每一段 JSON 回显均为逐行照抄的实测原文。
一、环境信息
本文实测环境与演示数据如下:
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS(x86_64) |
| Inventaire 版本 | 5.1.1(/api/config 返回 version: 5.1.1、serverGitHeadRev: 3ba29ff) |
| 服务监听端口 | 8138(Compose 套件宿主映射端口,实例内部端口 3016) |
| 数据库 | CouchDB 3.4.2 |
| 搜索引擎 | Elasticsearch 7.17.28 |
| 演示账号 | admin / q1w2e3r4(本文全部写操作实测账号) |
| admin 用户 ID | 1a152d5da00142ddbd825f0bbf000737 |
| 演示用户 | demo2(ID 1a152d5da00142ddbd825f0bbf001cca)、demo3、demo4 |
| 演示条目 | 实体 wd:Q110418801(《Introduction to Algorithms》)的 3 件库存条目 |
演示条目中的 wd: 前缀是 Inventaire 的实体 URI 形态,指向 Wikidata 的版本实体(edition);/api/config 中 remoteEntitiesOrigin: https://inventaire.io 表示远端实体数据来自旗舰实例。实体与 Wikidata 的关系、/api/config 全量字段语义见 《Inventaire 5.1.1 配置参数详解》第五章,本篇只从 API 调用视角使用它们。
二、前置条件
2.1 可访问的服务端点
本文所有请求面向一台已部署完成的 Inventaire 5.1.1 实例。部署方式任选其一:源码安装(见官方 Wiki)或 Compose 套件(推荐,完整步骤见 《Inventaire 5.1.1 Docker Compose 部署指南》)。部署完成后先做两个基础验证:
# 验证服务存活:匿名请求 /api/config(公开端点)
curl -s http://localhost:8138/api/config
实测回显(节选,实测首部):
{"version":"5.1.1","softwareName":"inventaire","serverGitHeadRev":"3ba29ff","env":"production","instanceName":"My Inventaire Instance","orgName":"Example Organization","orgUrl":"https://inventaire.example.org","remoteEntitiesOrigin":"https://inventaire.io","mapTiles":{"tileUrl":"https://tile.openstreetmap.org/{z}/{x}/{y}.png","attribution":"Map data & imagery © <a href=\"http://openstreetmap.
/api/config 无需认证,返回实例版本与公开信息,是 API 可用性的第一探针。
# 验证版本与端口:确认响应头
curl -sI http://localhost:8138/api/config | head -6
实测回显:
HTTP/1.1 200 OK
access-control-allow-origin: *
access-control-allow-methods: *
access-control-allow-headers: content-type
Content-Type: application/json; charset=utf-8
2.2 客户端工具
本文全部示例使用 curl(Ubuntu 22.04 自带)。涉及会话认证的示例统一用 -c/-b 读写 cookie 文件:
# 会话 cookie 文件路径(本文示例统一使用)
CJ=/tmp/inv-cookies.txt
⚠️ 注意:Inventaire 的会话 cookie 名含冒号(
inventaire:production:session),部分老版本 curl 需要 cookie 文件为 Netscape 格式(curl -c默认输出即该格式),不要手工改写 cookie 文件里的名字。
2.3 演示数据准备
本文 items 域示例基于以下两步准备的数据(命令均在实测环境执行过):
# 登录演示账号
curl -s -c $CJ -X POST http://localhost:8138/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"q1w2e3r4"}'
实测回显:
{"ok":true}
# 创建一件库存条目(entity 指向 Wikidata 版本实体)
curl -s -b $CJ -X POST http://localhost:8138/api/items \
-H 'Content-Type: application/json' \
-d '{"entity":"wd:Q110418801","details":"演示藏书第一册","transaction":"giving","visibility":["public"]}'
实测回显(节选,实测首尾):
{"item":{"_id":"1a152d5da00142ddbd825f0bbf0026df","_rev":"1-f56f502c8b776a7411651836d40ef9d1","entity":"wd:Q110418801","details":"演示藏书第一册","transaction":"giving","shelves":[],"owner":"1a152d5da00142ddbd825f0bbf000737","created":1792784484078,"snapshot":{"entity:title":"Introduction to Algorithms","entity:lang":"en","entity:worksUris":["wd:Q1141518"],"entity:authorsUris":["wd:Q2524992","wd:Q93028","wd:Q578036","wd:Q2024396"],"entity:image":"/img/entities/8c4fefb12d69cc7b80fc1d47d4213619b1aa80f0","entity:isbn13":"978-0-262-03384-8","entity:isbn10":"0-262-03384-4"},"busy":false}
后文统一用「admin 的用户 ID」「条目 _id」指代上述实测值,读者照做时应替换为自己实例中实际返回的值——_id 由 CouchDB 随机生成,不会与本文相同;entity 的 wd: URI 是全球唯一的 Wikidata 标识,可直接复用。
三、API 总览
3.1 路由机制
Inventaire 服务端所有 API 挂在 /api/ 前缀下,路由规则为「/api/<域> + 可选「/<端点名>」」,端点名与动作二选一:
- 新式路径端点:
GET /api/items/by-ids?ids=...——端点名写进路径; - 旧式 query action:
GET /api/items?action=by-ids&ids=...——等价,但响应会附带废弃警告;
新旧两种形态的等价性与警告文案均为实测:
curl -s -b $CJ "http://localhost:8138/api/users?action=by-usernames&usernames=admin" | python3 -m json.tool | tail -4
实测回显(节选,实测尾部):
"roles": []
}
响应末尾携带:
"warnings": ["Action parameters are deprecated: instead of /api/users?action=by-usernames, use /api/users/by-usernames"]
即官方明确提示迁移到路径端点形态。本文所有示例一律用新式路径端点;仅在「错误码实测」中保留旧式形态的警告原文作为对照。
此外路由层还有两条实测确认的行为:
HEAD请求按GET处理(源码api_routes.ts中if (method === 'head') method = 'get'),因此curl -I可用于无副作用探测;- 访问不存在的域或端点返回统一 404 结构(见 3.4 节错误示例)。
3.2 域清单
Inventaire 5.1.1 服务端注册的 API 域全表如下(据源码 server/controllers/api_routes.ts 的 apiControllers 注册表逐项核对,动作名为源码定义):
| 域 | 类型与核心动作 | 说明 |
|---|---|---|
| auth | GET username-availability/email-availability/wikidata-oauth;POST signup/login/logout/reset-password/email-confirmation;PUT password | 注册、登录、会话 |
| token | GET validation-email/reset-password | 邮箱令牌校验 |
| user | 当前登录用户资料与设置 | 单用户自助 |
| users | GET by-ids/by-accts/by-anonymizable-ids/by-usernames/search-by-position/nearby/by-creation-date | 用户查询 |
| items | GET by-ids/by-users/by-entities/by-user-and-entities/inventory-view/recent-public/last-public/search/nearby/by-bbox/export;POST 创建/bulk/delete;PUT 更新/bulk | 库存条目核心域 |
| entities | GET by-uris/reverse-claims/author-works/serie-parts/publisher-publications/images/popularity/languages/history/contributions/duplicates/enrich;POST 创建;PUT update-claim/update-label;DELETE | 实体数据(书籍/作者/版本) |
| shelves | GET by-ids/by-owners;POST 创建/delete;PUT 更新/add-items/remove-items | 书架 |
| groups | GET 默认/by-id/by-slug/slug/search-by-position;POST 创建;PUT invite/accept/decline/request/cancel-request/accept-request/refuse-request/make-admin/kick/leave/update-settings | 共享小组 |
| search | GET 默认/explain | 全局搜索 |
| feeds | GET 默认 | RSS 订阅源 |
| notifications | GET 默认 | 通知 |
| transactions | GET 默认 | 借还事务 |
| relations | GET 默认 | 好友关系 |
| config | GET 默认 | 实例公开配置 |
| listings | 列表(listings) | 收藏清单 |
| oauth | OAuth 客户端 | 第三方接入 |
| activitypub | 联邦活动 | 非联邦模式不可用(实测 404,见 5.9 节) |
| images / i18n / invitations / instances / reports / feedback / submit / tests | 辅助域 | 图片上传、多语言、邀请、上报等 |
本文第五章逐域实测其中的核心域:items、entities、users、groups、shelves、search、feeds、notifications/transactions/relations、config;listings、oauth 等低频域给出动作清单不展开实测。
3.3 认证方式
实测确认 Inventaire 5.1.1 支持三种 API 认证方式:
| 方式 | 用法 | 实测结果 |
|---|---|---|
| 会话 cookie | POST /api/auth/login 后用 -b cookie文件 携带 | 可用,官方前端同款方式 |
| HTTP Basic | curl -u 用户名:密码 | 可用,写操作实测通过 |
| feeds 令牌 | requester=<用户ID>&token=<readToken>(仅 feeds 域) | 可用,专为 RSS 阅读器设计 |
💡 技术拓展:Web 应用的 API 认证常见选项对比——
方式 原理 适用场景 Session Cookie 登录后服务端下发会话 cookie,后续请求自动携带 浏览器前端、同源脚本 HTTP Basic 每次请求头带 Authorization: Basic base64(用户:密码)简单脚本、一次性调试 Bearer/JWT Token 服务端签发无状态令牌,请求头携带 移动端、第三方 API 接入 专用读令牌 为特定端点签发的低权限长有效期令牌 RSS 阅读器等受限只读场景 Inventaire 实际采用「Session Cookie + HTTP Basic + feeds 专用读令牌」三轨制,未提供通用 Bearer API token;
readToken只能配合 feeds 域的requester参数使用(见 5.7 节实测),不能当作通用认证头。
3.4 统一错误结构
所有错误响应都是单一 JSON 对象,字段固定为 status(HTTP 状态码同值)、message、error_type、error_name、context(含请求 URL 等定位信息);部分错误还带 warnings 数组。实测四种典型错误:
# 访问不存在的端点形态(items 域 GET 没有 default 动作)
curl -s "http://localhost:8138/api/items?user=1a152d5da00142ddbd825f0bbf000737"
实测回显:
{"status":404,"message":"unknown action","context":{"method":"GET","query":{"user":"1a152d5da00142ddbd825f0bbf000737"},"body":{},"url":"/api/items?user=1a152d5da00142ddbd825f0bbf000737"}}
# 未认证访问需登录端点
curl -s "http://localhost:8138/api/notifications"
实测回显:
{"status":401,"message":"unauthorized api access","context":{"roles":["public"],"requiredAccessLevel":"authentified","url":"/api/notifications"}}
# 缺必填参数
curl -s "http://localhost:8138/api/feeds"
实测回显:
{"status":400,"message":"missing parameter in query: user or group or shelf","error_type":"missing_parameter","error_name":"missing_user or group or shelf","context":{"url":"/api/feeds"}}
# 搜索类型参数非法
curl -s "http://localhost:8138/api/search?search=algorithms&types=entities&filter=inv"
实测回显:
{"status":400,"message":"invalid types: entities (possible values: works, humans, genres, publishers, series, collections, genres, movements, languages, users, groups, shelves, lists)","context":{"value":"entities","url":"/api/search?search=algorithms&types=entities&filter=inv"}}
error_name 是程序化处理错误的最佳依据(如 missing_parameter、unauthorized),context.url 可直接定位出错请求。
3.5 限流约定
实测确认两类限流:
- 注册防滥用限流:连续两次
/api/auth/signup请求间隔过短触发 429,实测回显:
{"status":429,"message":"Too little time between requests","context":{"url":"/api/auth/signup"}}
- 搜索端点限流:源码
search.ts中两个端点均声明rateLimiting: { maxRequests: 50, restrictedRequestersOnly: true },即默认窗口内最多 50 次请求,且仅限本实例注册用户(restricted requesters)。实测在文档写作过程中的高频搜索调用(100+ 次)未触发 429,说明限流窗口按更长周期计数,正常脚本调用无需担心。
四、认证与首次调用
4.1 登录获取会话
POST /api/auth/login 是全 API 的入口,请求体为 JSON,返回体极简但响应头携带完整会话:
curl -si -c /tmp/inv-cookies.txt -X POST http://localhost:8138/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"q1w2e3r4"}'
实测回显(完整响应头与响应体):
HTTP/1.1 200 OK
access-control-allow-origin: *
access-control-allow-methods: *
access-control-allow-headers: content-type
Set-Cookie: loggedIn=true; Max-Age=15552000; Path=/; Expires=Thu, 22 Apr 2027 00:40:14 GMT
Set-Cookie: inventaire:production:session=eyJ0aW1lc3RhbXAiOjE3OTI4MDI0MTMyMDksInBhc3Nwb3J0Ijp7InVzZXIiOiIxYTE1MmQ1ZGEwMDE0MmRkYmQ4MjVmMGJiZjAwMDczNyJ9fQ==; path=/; expires=Thu, 22 Apr 2027 00:40:14 GMT; samesite=lax; httponly
Set-Cookie: inventaire:production:session.sig=vVH9SInUXzwugaQlPzOxWytNy4N3xmoSlzQb0Vpda0M; path=/; expires=Thu, 22 Apr 2027 00:40:14 GMT; samesite=lax; httponly
Content-Type: application/json; charset=utf-8
Content-Length: 11
ETag: W/"b-Ai2R8hgEarLmHKwesT1qcY913ys"
Date: Sat, 24 Oct 2026 00:40:14 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"ok":true}
逐项解读:
- 会话由三个 cookie 组成:
loggedIn=true(前端标记)、inventaire:production:session=<base64 载荷>(含用户 ID)、inventaire:production:session.sig=<签名>(防篡改); Max-Age=15552000即 180 天有效期;httponly+samesite=lax防脚本窃取与跨站发送;- 响应体仅
{"ok":true},用户详情需再查/api/user或 users 域。
4.2 HTTP Basic 认证
不维护 cookie 的脚本场景直接用 Basic:
curl -s -u admin:q1w2e3r4 "http://localhost:8138/api/items/by-users?users=1a152d5da00142ddbd825f0bbf000737" | head -c 200
实测回显:
{"items":[{"_id":"1a152d5da00142ddbd825f0bbf005b5d","_rev":"1-9af68597781c5264d5fde50e9bc58bf1","entity":"wd:Q110418801","details":"第二件演示物品","transaction":"inventorying","visibility":[],
返回了与 cookie 会话完全一致的登录态数据,确认 Basic 认证对所有需登录端点等效。
4.3 未认证访问的行为
未认证访问「需要登录」的端点返回 401(如 3.4 节 /api/notifications);但访问「数据类」端点时不报 401,而是降级为匿名可见性——只能看到公开数据。实测对比:同一 by-users 查询,登录态返回 3 件条目(含私有),匿名只返回 1 件公开条目:
curl -s "http://localhost:8138/api/items/by-users?users=1a152d5da00142ddbd825f0bbf000737" | python3 -c "import json,sys; d=json.load(sys.stdin); print('total:', d['total'])"
实测回显:
total: 1
这一行为是 visibility 权限模型在 API 层的体现(公开/网络/私有三级,语义见 《Inventaire 5.1.1 配置参数详解》第五章),脚本调试时若发现「查到的数据比界面少」,先检查认证是否生效。
4.4 公开端点首次调用
无需登录即可调用的端点,以用户名可用性检查为例:
curl -s "http://localhost:8138/api/auth/username-availability?username=nobody12345"
实测回显:
{"username":"nobody12345","status":"available"}
4.5 登出
POST /api/auth/logout 销毁会话:
curl -s -b /tmp/inv-cookies.txt -c /tmp/inv-cookies.txt -X POST http://localhost:8138/api/auth/logout
实测回显:
Found. Redirecting to /
登出为 302 重定向(服务端同时清空 cookie)。登出后沿用旧 cookie 访问数据端点,实测降级为匿名视角(by-users 的 total 从 3 变回 1),确认会话确实失效。
五、各 API 域详解
本章按域展开,每域覆盖「端点 → 方法 → 参数 → 请求示例 → 实测响应 → 错误形态」。所有 ID 类实测值(_id/owner 等)来自本文实测实例,读者操作时以自己实例的返回值为准。
5.1 items 域:库存条目
items 域是 Inventaire 的核心业务域,每件实体物品(书/影碟/游戏)的一个物理副本是一件 item。
5.1.1 按用户查询:GET /api/items/by-users
参数 users(复数,可逗号分隔多个用户 ID)、可选 limit、offset、context(可见性过滤)、with-users(是否附带用户资料,默认 false):
curl -s -b $CJ "http://localhost:8138/api/items/by-users?users=1a152d5da00142ddbd825f0bbf000737&limit=1&offset=0" | python3 -c "import json,sys; d=json.load(sys.stdin); print('keys:', list(d.keys())); print('total:', d['total'], 'offset:', d['offset'], 'returned:', len(d['items']))"
实测回显:
keys: ['items', 'total', 'offset']
total: 3 offset: 0 returned: 1
返回结构为 {items, total, offset}:total 是全量可见条目数,items 是当前页。单条 item 的完整结构(实测,登录态):
{"_id":"1a152d5da00142ddbd825f0bbf005b5d","_rev":"1-9af68597781c5264d5fde50e9bc58bf1","entity":"wd:Q110418801","details":"第二件演示物品","transaction":"inventorying","visibility":[],"shelves":[],"owner":"1a152d5da00142ddbd825f0bbf000737","created":1792784782207,"busy":false,"snapshot":{"entity:title":"Introduction to Algorithms","entity:lang":"en","entity:worksUris":["wd:Q1141518"],"entity:authorsUris":["wd:Q2524992","wd:Q93028","wd:Q578036","wd:Q2024396"],"entity:seriesUris":[],"entity:authors":"Thomas H. Cormen, Charles E. Leiserson, Ron Rivest, Clifford Stein","entity:image":"/img/entities/8c4fefb12d69cc7b80fc1d47d4213619b1aa80f0","entity:isbn13":"978-0-262-03384-8","entity:isbn10":"0-262-03384-4"}}
关键字段解读:
entity:关联的实体 URI(wd:Wikidata 版本实体或inv:本地实体);_rev:CouchDB 文档修订号,更新时会变化(见 5.1.4);transaction:流转状态,实测确认的取值有inventorying(自留)、giving(赠送)、lending(可借)、selling(出售);visibility:可见性数组,空数组即私有;snapshot:服务端从关联实体冗余出的展示字段(标题/作者/ISBN/封面),查询时无需再请求 entities 域。
5.1.2 按 ID 批量查询:GET /api/items/by-ids
参数 ids 可逗号分隔多个 ID,返回结构同上:
curl -s -b $CJ "http://localhost:8138/api/items/by-ids?ids=1a152d5da00142ddbd825f0bbf0026df,1a152d5da00142ddbd825f0bbf005b5d" | python3 -c "import json,sys; d=json.load(sys.stdin); print('total:', d['total'], 'ids:', [i['_id'][-6:] for i in d['items']])"
实测回显:
total: 2 ids: ['f0026df', 'f005b5d']
5.1.3 条目搜索:GET /api/items/search
参数 search(关键词,匹配 snapshot 冗余字段)、user/group/shelf 三选一(限定搜索范围)、可选 limit/offset。缺范围参数直接 400:
curl -s "http://localhost:8138/api/items/search?search=Algorithms"
实测回显:
{"status":400,"message":"missing parameter in query: user or group or shelf","error_type":"missing_parameter","error_name":"missing_user or group or shelf","context":{"url":"/api/items/search?search=Algorithms"}}
补上 user 后实测:
curl -s -b $CJ "http://localhost:8138/api/items/search?search=Algorithms&user=1a152d5da00142ddbd825f0bbf000737&limit=1&offset=0" | python3 -c "import json,sys; d=json.load(sys.stdin); print('keys:', list(d.keys())); print('total:', d['total'], 'first:', d['items'][0]['_id'], d['items'][0]['details'])"
实测回显:
keys: ['items', 'total', 'continue'] total: 3 first: 1a152d5da00142ddbd825f0bbf0026df 演示藏书第一册
注意 items/search 的分页返回是 {items, total, continue}(continue 为 CouchDB 游标),与 by-users 的 {items, total, offset} 不同(见第六章)。
5.1.4 创建、更新与删除
创建:POST /api/items,必填 entity,常用可选 details(备注)、transaction、visibility:
curl -s -b $CJ -X POST http://localhost:8138/api/items \
-H 'Content-Type: application/json' \
-d '{"entity":"wd:Q110418801","details":"API 文档实测创建条目","transaction":"inventorying","lang":"en"}'
实测回显(节选,实测首尾):
{"item":{"_id":"1a152d5da00142ddbd825f0bbf0078d2","_rev":"1-86ac1cffa9e84f71477b8958494696eb","entity":"wd:Q110418801","details":"API 文档实测创建条目","transaction":"inventorying","visibility":[],"shelves":[],"owner":"1a152d5da00142ddbd825f0bbf001cca","created":1792786694733,"snapshot":{"entity:title":"Introduction to Algorithms","entity:isbn13":"978-0-262-03384-8","entity:isbn10":"0-262-03384-4"}},"warnings":["unexpected parameter: \"lang\""]}
创建即返回完整 item 对象;lang 不是合法参数,服务端以 warnings 提示但不拒绝请求——entity 的语言信息由关联实体自带,创建 item 时不需要也不能指定。缺 entity 则 400:
{"status":400,"message":"missing parameter in body: entity","error_type":"missing_parameter","error_name":"missing_entity","context":{"url":"/api/items"}}
批量创建:POST /api/items/bulk,请求体为 items 数组:
curl -s -b $CJ -X POST http://localhost:8138/api/items/bulk \
-H 'Content-Type: application/json' \
-d '{"items":[{"entity":"wd:Q110418801","details":"bulk 批量第 1 件","transaction":"inventorying"},{"entity":"wd:Q110418801","details":"bulk 批量第 2 件","transaction":"lending"}]}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); print('created:', [i['_id'] for i in d['items']])"
实测回显:
created: ['1a152d5da00142ddbd825f0bbf00c1be', '1a152d5da00142ddbd825f0bbf00c831']
更新:PUT /api/items,请求体必填 id(注意是单数 id,不是数组),其余字段为要修改的项:
curl -s -b $CJ -X PUT http://localhost:8138/api/items \
-H 'Content-Type: application/json' \
-d '{"id":"1a152d5da00142ddbd825f0bbf0080f9","details":"API 更新后备注","transaction":"giving"}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); i=d['item']; print('rev:', i['_rev']); print('details:', i['details']); print('updated:', i.get('updated'))"
实测回显:
rev: 2-45aad8a649483ce2c252fa1b1bc8999d
details: API 更新后备注
updated: 1792786732394
_rev 从 1- 升到 2-,并新增 updated 时间戳。缺 id 时 400(实测错误名 missing_id):
{"status":400,"message":"missing parameter in body: id","error_type":"missing_parameter","error_name":"missing_id","context":{"url":"/api/items"},"warnings":["unexpected parameter: \"ids\""]}
越权修改:非 owner 修改他人条目,实测返回 400(不是 403):
# 以另一账号 demo2 登录并保存会话
curl -s -c /tmp/inv-cookies-demo2.txt -X POST http://localhost:8138/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"demo2","password":"q1w2e3r4"}'
# 用 demo2 的会话修改 admin 的条目
curl -s -b /tmp/inv-cookies-demo2.txt -X PUT http://localhost:8138/api/items \
-H 'Content-Type: application/json' \
-d '{"id":"1a152d5da00142ddbd825f0bbf0026df","details":"越权修改测试"}'
实测回显:
{"status":400,"message":"user is not item owner","context":{"userId":"1a152d5da00142ddbd825f0bbf001cca","ownerId":"1a152d5da00142ddbd825f0bbf000737","url":"/api/items"}}
删除:POST /api/items/delete,请求体 ids 为 JSON 数组:
curl -s -b $CJ -X POST http://localhost:8138/api/items/delete \
-H 'Content-Type: application/json' \
-d '{"ids":["1a152d5da00142ddbd825f0bbf00c1be","1a152d5da00142ddbd825f0bbf00c831"]}'
实测回显:
{"ok":true}
ids 误传逗号分隔字符串会 400:
{"status":400,"message":"invalid id: 1a152d5da00142ddbd825f0bbf00c1be,1a152d5da00142ddbd825f0bbf00c831","error_type":"invalid_parameter","error_name":"invalid_id","context":{"parameter":"id","value":"1a152d5da00142ddbd825f0bbf00c1be,1a152d5da00142ddbd825f0bbf00c831","type":"string","config":{},"url":"/api/items/delete"}}
旧式 ?action= 形态删除仍可用,但响应带两条废弃警告(实测原文):
{"ok":true,"warnings":["Action parameters are deprecated: instead of /api/items?action=delete-by-ids, use /api/items/delete-by-ids","Deprecated: the endpoint \"POST /api/items/delete-by-ids\" was moved to \"POST /api/items/delete\""]}
5.1.5 库存总览视图:GET /api/items/inventory-view
参数 user/group/shelf 三选一,返回按作者/类型/主题/归属者组织的树状聚合:
curl -s -b $CJ "http://localhost:8138/api/items/inventory-view?user=1a152d5da00142ddbd825f0bbf000737" | head -c 400
实测回显(节选,实测首部):
{"worksTree":{"author":{"wd:Q2524992":["wd:Q1141518"],"wd:Q93028":["wd:Q1141518"],"wd:Q578036":["wd:Q1141518"],"wd:Q2024396":["wd:Q1141518"]},"genre":{"wd:Q83790":["wd:Q1141518"]},"subject":{"wd:Q8366":["wd:Q1141518"]},"owner":{"1a152d5da00142ddbd825f0bbf000737":{"wd:Q1141518":["1a152d5da00142ddbd825f0bbf0026df"]}}},"workUriItemsMap":{"wd:Q1141518"
worksTree 是「维度 → 实体 URI → 作品 URI → 条目 ID 列表」的嵌套映射,前端书架页的分组展示即基于此端点。
5.1.6 导出:GET /api/items/export
参数仅 format(allowlist:csv/json)。导出对象固定为请求者自己的全部条目,不接受 user 参数(传了会被忽略并警告):
curl -s -b $CJ "http://localhost:8138/api/items/export?format=csv"
实测回显(表头行 + 3 条数据行中的第 1 条):
Item URL,Item details,Item notes,Item visibility,Item transaction,Item created,Shelves,Edition URL,Edition ISBN-13,Edition ISBN-10,Edition title,Edition subtitle,Edition publication date,Edition cover,Edition number of pages,Edition language,Works URLs,Works labels,Original language,Works Series ordinals,Authors URLs,Authors labels,Translators labels,Translators URLs,Series URLs,Series labels,Genres URLs,Genres labels,Subjects URLs,Subjects labels,Publisher URLs,Publisher label
http://localhost:8138/items/1a152d5da00142ddbd825f0bbf0026df,演示藏书第一册,,public,giving,2026-10-23T19:41:24.078Z,,http://localhost:8138/entity/wd:Q110418801,978-0-262-03384-8,0-262-03384-4,Introduction to Algorithms,,2009,http://localhost:8138/img/entities/8c4fefb12d69cc7b80fc1d47d4213619b1aa80f0,,English,http://localhost:8138/entity/wd:Q1141518,Introduction to Algorithms,English,,"http://localhost:8138/entity/wd:Q2524992,http://localhost:8138/entity/wd:Q93028,http://localhost:8138/entity/wd:Q578036,http://localhost:8138/entity/wd:Q2024396","Thomas H. Cormen, Charles E. Leiserson, Ron Rivest, Clifford Stein",,,,,http://localhost:8138/entity/wd:Q83790,textbook,http://localhost:8138/entity/wd:Q8366,algorithm,http://localhost:8138/entity/wd:Q73820,MIT Press
两个实测确认的行为细节:
- 缺
format参数 400:{"status":400,"message":"missing parameter in query: format","error_type":"missing_parameter","error_name":"missing_format","context":{"url":"/api/items/export?user=1a152d5da00142ddbd825f0bbf000737"},"warnings":["unexpected parameter: \"user\""]}(注意user同时被标记为意外参数); format=json实测返回与 CSV 完全相同的内容(响应头同为text/csv)——5.1.1 版本的导出实现只有 CSV 一种落地格式,json虽在参数白名单里但走同一 CSV 分支,依赖 JSON 导出的脚本需自行转换。
5.1.7 公开动态:GET /api/items/recent-public
无需认证,返回全实例最新的公开条目:
curl -s "http://localhost:8138/api/items/recent-public" | head -c 250
实测回显(节选,实测首部):
{"items":[{"_id":"1a152d5da00142ddbd825f0bbf0026df","_rev":"1-f56f502c8b776a7411651836d40ef9d1","entity":"wd:Q110418801","details":"演示藏书第一册","transaction":"giving","shelves":[],"owner":"1a152d5da00142ddbd825f0bbf000737","created":1792784484078,"snapshot":{"entity:title":"Introduction t
5.2 entities 域:实体数据
entities 域管理书籍、作者、作品、出版社等「元数据实体」,是 items 域的数据底座。本地实例的实体分两类来源:wd: 前缀(Wikidata 同步的远端实体)与 inv: 前缀(本实例自建实体)。
5.2.1 按 URI 批量取实体:GET /api/entities/by-uris
参数 uris(逗号分隔多个实体 URI)。以本文演示版本实体为例:
curl -s "http://localhost:8138/api/entities?action=by-uris&uris=wd:Q110418801" -o /tmp/inv-entity.json
python3 -c "import json; e=json.load(open('/tmp/inv-entity.json'))['entities']['wd:Q110418801']; print('uri:', e['uri'], '| type:', e['type']); print('labels.en:', e['labels'].get('en')); print('descriptions.en:', e['descriptions'].get('en')); print('claims keys:', list(e['claims'].keys())[:6]); print('top keys:', list(e.keys()))"
实测回显:
uri: wd:Q110418801 | type: edition
labels.en: Introduction to Algorithms
descriptions.en: 3rd edition hardcover book
claims keys: ['wdt:P31', 'wdt:P123', 'wdt:P212', 'wdt:P243', 'wdt:P407', 'wdt:P577', 'wdt:P629', 'wdt:P957', 'wdt:P1144', 'wdt:P1476']
top keys: ['uri', 'wdId', 'type', 'labels', 'aliases', 'descriptions', 'sitelinks', 'claims', 'originalLang', 'lastrevid', 'invId', 'invRev', 'image']
结构解读:
type取值edition(版本)/work(作品)/human(作者)/publisher/collection/serie等;claims是 Wikidata 属性声明(wdt:P212ISBN-13、wdt:P1476标题、wdt:P577出版日期等);image.url为相对路径(实测{'url': '/img/entities/8c4fefb12d69cc7b80fc1d47d4213619b1aa80f0'}),拼接实例域名即封面地址。
5.2.2 远端实体的搜索限制
Wikidata 远端实体不能在本地搜索端点中检索(本地 Elasticsearch 只索引本地与已缓存实体),实测:
curl -s "http://localhost:8138/api/search?search=algorithms&types=works"
实测回显:
{"status":400,"message":"entities should be searched on remote instance","context":{"types":["works"],"remoteEntitiesOrigin":"https://inventaire.io","url":"/api/search?search=algorithms&types=works"}}
context.remoteEntitiesOrigin 指明了应去哪里搜索(旗舰实例),脚本可通过 /api/config 动态获取该值。
5.2.3 关联实体查询
entities 域还提供按关系查关联实体的动作(参数均为实体 URI):
| 端点 | 作用 |
|---|---|
GET /api/entities/author-works?uri=<human uri> | 作者的全部作品 |
GET /api/entities/serie-parts?uri=<serie uri> | 丛编的全部分册 |
GET /api/entities/publisher-publications?uri=<publisher uri> | 出版社的全部出版物 |
GET /api/entities/reverse-claims?property=<P属性>&value=<值> | 反向声明查询(如按 ISBN 反查实体) |
GET /api/entities/history?uri=<uri> | 实体编辑历史 |
以上动作在源码 localEntitiesControllers 中注册,参数校验与 by-uris 一致;wd: 远端实体的这些查询会代理到 remoteEntitiesOrigin 旗舰实例。
5.3 users 域:用户查询
5.3.1 按用户名查询:GET /api/users/by-usernames
参数 usernames(逗号分隔)。登录态与匿名的返回字段差异极大,实测对照:
# 登录态查询(自己)
curl -s -b $CJ "http://localhost:8138/api/users/by-usernames?usernames=admin" | python3 -m json.tool
实测回显(完整):
{
"users": {
"admin": {
"_id": "1a152d5da00142ddbd825f0bbf000737",
"_rev": "8-4d0a883c93d28b222b017ffb59b09cb9",
"type": "user",
"username": "admin",
"created": 1792758754721,
"anonymizableId": "1c93099df5d340e884ad232118d589ed",
"email": "admin@inventaire.example.org",
"readToken": "S4T17x1n6xR1HWxYeOATkacGLldFdk27",
"settings": {"notifications": {}},
"snapshot": {
"private": {"items:count": 2, "items:last-add": 1792784782207},
"network": {"items:count": 0},
"public": {"items:count": 1, "items:last-add": 1792784484078}
},
"validEmail": false,
"accessLevels": [],
"enabledOAuth": [],
"roles": []
}
}
}
# 匿名查询同一用户
curl -s "http://localhost:8138/api/users/by-usernames?usernames=admin" | python3 -m json.tool
实测回显(完整):
{
"users": {
"admin": {
"_id": "1a152d5da00142ddbd825f0bbf000737",
"username": "admin",
"created": 1792758754721,
"snapshot": {
"public": {"items:count": 1, "items:last-add": 1792784484078}
}
}
}
}
关键差异:登录态多出 email、readToken、settings、anonymizableId 与 snapshot 的 private/network 两级统计;匿名只保留 public 统计。readToken 是 feeds 域专用令牌,只在登录态查询自己时可见(安全设计:他人查不到你的 readToken)。
5.3.2 用户搜索的正确姿势
users 域没有 search 动作,误用直接 404:
curl -s "http://localhost:8138/api/users/search?search=demo"
实测回显:
{"status":404,"message":"unknown action","context":{"endpointName":"search","url":"/api/users/search?search=demo"}}
用户搜索要走 search 域的 types=users(见 5.6 节)。其余动作(by-accts 按 username@instance 联邦账号查、by-creation-date 按注册时间查、nearby/search-by-position 按地理位置查)参数形态与 by-usernames 同类。
5.4 groups 域:共享小组
5.4.1 创建小组:POST /api/groups
curl -s -b $CJ -X POST http://localhost:8138/api/groups \
-H 'Content-Type: application/json' \
-d '{"name":"api-doc-group","description":"D5 API 篇实测小组","searchable":true}'
实测回显(完整):
{"type":"group","name":"api-doc-group","description":"D5 API 篇实测小组","searchable":true,"admins":[{"user":"1a152d5da00142ddbd825f0bbf000737","invitor":null,"timestamp":1792802418513}],"members":[],"invited":[],"declined":[],"requested":[],"position":null,"open":false,"creator":"1a152d5da00142ddbd825f0bbf000737","created":1792802418513,"slug":"api-doc-group","_id":"1a152d5da00142ddbd825f0bbf00aea6","_rev":"1-4673d5725efa4dcc8607e43de17cc5b0"}
创建者自动进入 admins;open: false 表示加入需邀请,searchable: true 表示可被搜索。
5.4.2 查询小组:GET /api/groups 与 GET /api/groups/by-id
GET /api/groups(无参数)返回当前登录用户所属的全部小组:
curl -s -b $CJ "http://localhost:8138/api/groups" | python3 -c "import json,sys; d=json.load(sys.stdin); print('groups:', [(g['name'], g['_id'][-6:]) for g in d['groups']])"
实测回显:
groups: [('demo-book-club', 'f004479'), ('api-doc-group', 'f00aea6')]
by-id 需要参数 id,返回组详情加组内用户资料:
curl -s -b $CJ "http://localhost:8138/api/groups/by-id?id=1a152d5da00142ddbd825f0bbf00aea6" | python3 -c "import json,sys; d=json.load(sys.stdin); print('keys:', list(d.keys())); print('group name:', d['group']['name'], '| users:', [u['username'] for u in d['users']])"
实测回显:
keys: ['group', 'users']
group name: api-doc-group | users: ['admin']
参数形态错误实测两种:缺 id → {"status":400,"message":"missing parameter in query: id","error_type":"missing_parameter","error_name":"missing_id","context":{"parameter":"id","value":"","type":"string","config":{"type":"group"},"url":"/api/groups/by-id?id="}};id 值非法 → error_name: invalid_id。
5.4.3 更新小组设置:PUT /api/groups/update-settings
参数 group(组 ID)、attribute(白名单:name/picture/description/searchable/position/open)、value。实测把 description 改掉:
curl -s -b $CJ -X PUT http://localhost:8138/api/groups/update-settings \
-H 'Content-Type: application/json' \
-d '{"group":"1a152d5da00142ddbd825f0bbf00aea6","attribute":"description","value":"D5 API 篇实测小组(更新于 API 文档写作时)"}'
实测回显:
{"ok":true,"update":{}}
update 字段用于返回需要前端同步的派生值(如改名后 slug 变化),本例改 description 无派生变化故为空对象。
5.4.4 成员管理动作
groups 域的 PUT 动作覆盖完整成员生命周期,参数统一为 group(组 ID)+ 可选 user(目标用户):
| 动作 | 作用 | 调用者 |
|---|---|---|
PUT /api/groups/invite | 邀请用户入组 | 管理员 |
PUT /api/groups/accept | 接受邀请 | 被邀请者 |
PUT /api/groups/decline | 拒绝邀请 | 被邀请者 |
PUT /api/groups/request | 申请入组 | 任意用户 |
PUT /api/groups/accept-request | 批准入组申请 | 管理员 |
PUT /api/groups/refuse-request | 拒绝入组申请 | 管理员 |
PUT /api/groups/cancel-request | 撤回入组申请 | 申请人 |
PUT /api/groups/make-admin | 提升为管理员 | 管理员 |
PUT /api/groups/kick | 移除成员 | 管理员 |
PUT /api/groups/leave | 退出小组 | 成员本人 |
leave 实测(退出本文创建的演示组):
curl -s -b $CJ -X PUT http://localhost:8138/api/groups/leave \
-H 'Content-Type: application/json' \
-d '{"group":"1a152d5da00142ddbd825f0bbf00aea6"}'
实测回显:
{"ok":true}
注意 groups 域没有删除动作:小组无成员后仍保留在库中,清理演示数据时应使用 leave 退出而非寻找删除端点。
5.5 shelves 域:书架
书架是条目的展示集合(如「想读」「在借」),一个 item 可属于多个 shelf。
创建:POST /api/shelves:
curl -s -b $CJ -X POST http://localhost:8138/api/shelves \
-H 'Content-Type: application/json' \
-d '{"name":"API 实测书架","description":"D5 文档演示书架","visibility":["public"]}'
实测回显(完整):
{"shelf":{"visibility":["public"],"name":"API 实测书架","description":"D5 文档演示书架","owner":"1a152d5da00142ddbd825f0bbf001cca","created":1792786798691,"_id":"1a152d5da00142ddbd825f0bbf008d63","_rev":"1-044511e97185f3b1c0d1d64622a187c0"}}
加入条目:PUT /api/shelves/add-items,参数 id(书架 ID)+ items(条目 ID 数组):
curl -s -b $CJ -X PUT http://localhost:8138/api/shelves/add-items \
-H 'Content-Type: application/json' \
-d '{"id":"1a152d5da00142ddbd825f0bbf008d63","items":["1a152d5da00142ddbd825f0bbf0091e2"]}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); s=list(d['shelves'].values())[0]; print('name:', s['name'], '| items:', s['items'])"
实测回显:
name: API 实测书架 | items: ['1a152d5da00142ddbd825f0bbf0091e2']
按归属者查询:GET /api/shelves/by-owners?owners=<用户ID>,返回 {shelves: {...}} 映射(书架 ID → 书架对象)。删除条目后书架中的引用自动清理,实测 by-owners 中已删条目不再出现在 items 列表。
删除:POST /api/shelves/delete,参数 ids 数组,实测回显含被删对象快照:
{"ok":true,"shelves":[{"_id":"1a152d5da00142ddbd825f0bbf008d63","_rev":"1-044511e97185f3b1c0d1d64622a187c0","visibility":["public"],"name":"API 实测书架","description":"D5 文档演示书架","owner":"1a152d5da00142ddbd825f0bbf001cca","created":1792786798691,"items":["1a152d5da00142ddbd825f0bbf0091e2"],"_deleted":true}]}
_deleted: true 是 CouchDB 删除标记,即软删除。
5.6 search 域:全局搜索
GET /api/search,参数 search(关键词)与 types(复数,可逗号分隔多值)。合法 types 全集(实测 400 报错原文给出):works, humans, genres, publishers, series, collections, genres, movements, languages, users, groups, shelves, lists。
搜用户(登录与否均可,实测匿名):
curl -s "http://localhost:8138/api/search?search=demo&types=users"
实测回显(完整):
{"results":[{"id":"1a152d5da00142ddbd825f0bbf005e95","type":"users","label":"demo3","_score":8.127738},{"id":"1a152d5da00142ddbd825f0bbf0066df","type":"users","label":"demo4","_score":8.127738},{"id":"1a152d5da00142ddbd825f0bbf001cca","type":"users","label":"demo2","_score":6.0844355}]}
_score 是 Elasticsearch 相关性得分。加 limit=1 实测截取第一页:
curl -s "http://localhost:8138/api/search?search=demo&types=users&limit=1"
实测回显:
{"results":[{"id":"1a152d5da00142ddbd825f0bbf005e95","type":"users","label":"demo3","_score":7.317114}]}
搜本地实体:types 传 humans/works 等实体类型时只会命中本地实体;Wikidata 远端实体不可搜(400,见 5.2.2 节)。搜群组、书架同理(types=groups、types=shelves,要求对应对象 searchable: true 或 visibility 含 public)。
参数误写的防御性提示:类型参数是复数 types,误写单数 type 时实测返回 400 missing_types 并附 warnings: ["unexpected parameter: \"type\""]——利用 warnings 能快速定位参数名拼写错误。
5.7 feeds 域:RSS 订阅源
GET /api/feeds 输出 RSS 2.0 XML,参数 user/group/shelf 三选一指定订阅对象。匿名访问只输出公开条目:
curl -s "http://localhost:8138/api/feeds?user=1a152d5da00142ddbd825f0bbf000737" | head -c 300
实测回显(节选,实测首部):
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[admin]]></title><description><![CDATA[admin]]></description><link>http://localhost:8138/users/1a152d5da00142ddbd825f0bbf000737</link><image><url>https://inventaire.io/public/ic
实测统计:该匿名 feed 含 <item> 元素 1 个(仅公开条目)。
要看到私有条目,需用 requester + token 组合(token 即 5.3.1 节拿到的 readToken):
curl -s "http://localhost:8138/api/feeds?requester=1a152d5da00142ddbd825f0bbf000737&token=S4T17x1n6xR1HWxYeOATkacGLldFdk27&user=1a152d5da00142ddbd825f0bbf000737" -o /tmp/inv-feed.xml
grep -o '<item>' /tmp/inv-feed.xml | wc -l
实测回显:
3
同一订阅对象,匿名 1 条、带令牌 3 条——requester+token 把可见性提升到请求者本人的权限级别,这是 RSS 阅读器订阅私有库存的唯一通道。
两个错误形态实测:
# 缺对象参数
curl -s "http://localhost:8138/api/feeds"
{"status":400,"message":"missing parameter in query: user or group or shelf","error_type":"missing_parameter","error_name":"missing_user or group or shelf","context":{"url":"/api/feeds"}}
# 带 token 却不带 requester(两者必须成对)
curl -s "http://localhost:8138/api/feeds?user=1a152d5da00142ddbd825f0bbf000737&token=S4T17x1n6xR1HWxYeOATkacGLldFdk27"
{"status":400,"message":"missing parameter in query: requester","error_type":"missing_parameter","error_name":"missing_requester","context":{"url":"/api/feeds?user=1a152d5da00142ddbd825f0bbf000737&token=S4T17x1n6xR1HWxYeOATkacGLldFdk27"}}
5.8 notifications / transactions / relations 域
三个社交辅助域均为 GET 默认端点、需登录,返回结构固定:
curl -s -b $CJ "http://localhost:8138/api/notifications"
实测回显(完整):
{"notifications":[],"total":0,"offset":0}
curl -s -b $CJ "http://localhost:8138/api/transactions"
实测回显(完整):
{"transactions":[]}
curl -s -b $CJ "http://localhost:8138/api/relations"
实测回显(完整):
{"friends":[],"userRequested":[],"otherRequested":[],"network":["1a152d5da00142ddbd825f0bbf000737"]}
解读:transactions 收录借还请求(lending/borrowing 状态流转的条目往来),好友确认后双方条目才互见;relations 的 network 是「好友的好友」集合,Inventaire 的社交模型为两层(好友 + 好友网络),无全局陌生人社交。匿名访问这三个端点均 401(见 3.4 节)。
5.9 config 与其他辅助域
config:GET /api/config 公开(完整语义见 《Inventaire 5.1.1 配置参数详解》第三章),API 调用视角最常用的两个字段是 version 与 remoteEntitiesOrigin。
token 域:GET /api/token/validation-email(邮箱验证链接落地)与 GET /api/token/reset-password(密码重置链接落地),由注册/找回邮件中的链接调用,脚本一般不直接使用。
activitypub 域:非联邦模式下不可用,实测:
curl -s "http://localhost:8138/api/activitypub"
实测回显:
{"status":404,"message":"unknown action","context":{"method":"GET","query":{},"body":{},"url":"/api/activitypub"}}
同源的联邦发现端点 webfinger 亦返回 404("message":"user is not on the fediverse"),确认单机实例默认关闭联邦。开启联邦模式后的 activitypub 端点形态不在本篇范围内(配置开关见 《Inventaire 5.1.1 配置参数详解》第五章联邦参数节)。
六、分页/过滤/排序通用约定
实测归纳 Inventaire API 的分页与过滤约定:
| 端点 | 分页参数 | 响应分页字段 | 说明 |
|---|---|---|---|
GET /api/items/by-users | limit、offset | total、offset | 经典偏移分页 |
GET /api/items/by-ids | 无(ids 本身即集合) | total、offset | offsets 恒为 0 |
GET /api/items/search | limit、offset | total、continue | CouchDB 游标 continue |
GET /api/search | limit、offset | results(无 total) | Elasticsearch 风格 |
GET /api/notifications | limit、offset | total、offset | 经典偏移分页 |
💡 技术拓展:API 分页常见两种风格对比——
风格 参数/字段 特点 偏移分页(offset) limit+offset,响应带total可跳页、数据变动时可能重复/漏项 游标分页(cursor) 响应带续读令牌(如 continue),下一页回传稳定不重漏、但不能跳页 Inventaire 两种并存:列表查询用 offset,CouchDB 视图查询(items/search)额外返回
continue游标;写批量拉取脚本时优先用total判断终止、offset步进,continue仅在需要严格一致性的遍历场景使用。
过滤约定:items 域的 context 参数可按可见性过滤(取值 public/network/private);search 域的 filter 参数配合 types 使用(如 types=users 时 filter=inv 只搜本实例用户)。
排序约定:列表端点默认按创建时间倒序(snapshot.items:last-add 与 created 时间戳的实测顺序一致);recent-public/last-public 是按时间排序的专用端点,无自定义排序参数。
七、常见问题
Q1:GET /api/items?user=<用户ID> 为什么返回 404 unknown action?
items 域的 GET 方法没有注册 default 动作,必须写明端点名。正确形态是 GET /api/items/by-users?users=<用户ID>(注意参数是复数 users,值为用户 ID,如本文实测的 1a152d5da00142ddbd825f0bbf000737)。这是从旧版文档迁移过来时最容易踩的坑,实测错误回显见 3.4 节。
Q2:响应里的 warnings 需要处理吗?
warnings 不影响请求成功,但几乎总是意味着写法不规范:unexpected parameter 提示参数名写错(如创建 item 传 lang)、Action parameters are deprecated 提示应迁移到路径端点。建议脚本把 warnings 打进日志,避免静默积累错误写法。
Q3:注册接口返回 429 Too little time between requests?
signup 有防滥用限流,两次注册请求间隔过短即触发(实测回显见 3.5 节)。等待一段时间后重试即可;批量创建测试账号时应在请求间加延时。
Q4:修改他人条目为什么返回 400 而不是 403?
Inventaire 把「资源不存在于你的名下」当作请求参数问题处理:user is not item owner(实测回显见 5.1.4 节)。写异常处理时不要依赖 403 判断越权,应检查 error_name 与 message。
Q5:/api/items/export?format=json 返回的还是 CSV?
是。实测确认 5.1.1 的导出控制器对 json 与 csv 走同一 CSV 分支(响应头 text/csv),JSON 导出是预留的参数白名单。需要 JSON 请用 GET /api/items/by-users?users=<自己ID> 自行组装。
Q6:搜索书籍返回 400 entities should be searched on remote instance?
本地实例的搜索索引只覆盖本地数据,Wikidata 实体(types=works/humans 等)的搜索代理到旗舰实例执行时,本地端点直接拒绝并提示远端地址(见 5.2.2 节)。在自托管实例上搜书前,先把目标书籍的条目创建过一次(创建过程会拉取并缓存实体),或直接对接 remoteEntitiesOrigin 指向的远端搜索。
Q7:feeds 的 token 参数怎么用?为什么带了 token 还报 missing requester?
token(即 readToken)必须与 requester=<该用户的ID> 成对出现,单独传 token 是无效请求(实测回显见 5.7 节)。完整订阅 URL 形态:/api/feeds?requester=<ID>&token=<readToken>&user=<ID>,可直接填入 RSS 阅读器。
Q8:登出后旧 cookie 还能用吗?
会话立即失效,但数据端点不报 401 而是降级匿名视角(实测 total 从 3 变 1,见 4.5 节)。脚本判断会话有效性不要依赖「是否 401」,应对比匿名与登录态的数据范围或直接查询 /api/user。
八、参考链接
- 本专栏姊妹篇:《Inventaire 5.1.1 介绍与选型指南》、《Inventaire 5.1.1 Docker Compose 部署指南》、《Inventaire 5.1.1 配置参数详解》
- 官网与旗舰实例(远端实体搜索的默认目标):https://inventaire.io
- 服务端主仓库(API 路由定义
server/controllers/api_routes.ts):https://codeberg.org/inventaire/inventaire - GitHub 镜像仓库:https://github.com/inventaire/inventaire
- 项目 Wiki:https://wiki.inventaire.io
- 官方 Docker 套件(本文实测环境的部署来源):https://github.com/inventaire/docker-inventaire
- 示例账号约定:本文档系列演示实例的注册账号为
admin,密码q1w2e3r4