LibreKB 2.3.3 API 使用详解
LibreKB 2.3.3 只读 JSON API 六端点 curl 全实测:X-API-Key/query 双认证、401/403/404/405/400/503 全错误码实录、可见性过滤、分页约定、维护模式联动与 CORS 预检。
LibreKB 2.3.3 API 使用详解
目标:LibreKB 2.3.3 只读 JSON API 的全部 6 个端点、认证方式、可见性规则与错误码逐条 curl 实测——从默认 503 到开启后 200、401/403/404/405/400/503 全分支回显照抄。实测环境:
http://192.168.1.200:8139(LibreKB 2.3.3 + PHP 8.4.27 + MySQL 8.0.46,2026-11-01)。
一、环境信息
- 软件:LibreKB 2.3.3(API 自 v2.2.0 引入,README 实查)
- API 前缀:
/api/v1,只读 JSON,Content-Type: application/json; charset=utf-8 - 认证:
X-API-Key请求头或api_key查询参数(密钥在后台 Settings→API 配置) - 测试数据:4 个分类(含 1 个禁用、1 个嵌套子分类)、5 篇文章(含 1 篇禁用、1 篇在禁用分类内)、1 个附件
- 演示密钥:
librekb-demo-key-2026;管理员账号admin@kb.example.com/q1w2e3r4 - 客户端工具:curl 8.18.0
二、前置条件
API 默认关闭,直接调用返回 503(实录):
$ curl -s -w "\nHTTP %{http_code}\n" http://192.168.1.200:8139/api/v1/categories
{"error":"The API is disabled."}
HTTP 503
开启方式(后台 Settings→API 分区,脚本化等价命令,csrf_token 从表单页提取):
$ curl -s -b cookies.txt -c cookies.txt -w "POST /admin/settings/api -> HTTP %{http_code}\n" \
http://192.168.1.200:8139/admin/settings/api \
--data-urlencode "csrf_token=..." \
--data-urlencode "api_enabled=enabled" \
--data-urlencode "api_key=librekb-demo-key-2026" \
--data-urlencode "api_cors_origin=*"
POST /admin/settings/api -> HTTP 302
三个设置项的取值规则与校验实测见《LibreKB 2.3.3 配置参数详解》(https://www.quzuo.net/document/0NMKA1ZoNL9x)API 分区一节。
三、API 总览
| 端点 | 方法 | 说明 |
|---|---|---|
/api/v1/articles/search | GET | 全文检索(标题+内容),分页,返回摘要+附件元数据 |
/api/v1/articles/{id} | GET | 按数字 ID 取文章(含清洗后 HTML 正文) |
/api/v1/articles/slug/{slug} | GET | 按 slug 取文章(含正文) |
/api/v1/categories | GET | 启用分类树(递归 children,含 article_count) |
/api/v1/categories/{id} | GET | 按 ID 取单分类(含子树与该分类文章列表) |
/api/v1/categories/slug/{slug} | GET | 按 slug 取单分类(同上) |
| 以上全部 | OPTIONS | CORS 预检,204 |
约定:仅接受 GET(其余方法 405);无内建限流;可见性与前台严格一致——禁用文章、禁用分类(含祖先被禁用)下的启用文章一律 404/省略;维护模式期间全部 503。响应头固定携带 X-Content-Type-Options: nosniff 与 Cache-Control: no-store。
四、认证与首次调用
设置密钥后,无 key 与错 key 都是 401(实录):
$ curl -s -w "\nHTTP %{http_code}\n" http://192.168.1.200:8139/api/v1/categories
{"error":"A valid API key is required."}
HTTP 401
$ curl -s -w "\nHTTP %{http_code}\n" -H "X-API-Key: nope-nope-nope-0000" \
http://192.168.1.200:8139/api/v1/categories
{"error":"A valid API key is required."}
HTTP 401
带正确密钥的首次调用:
$ curl -s -w "\nHTTP %{http_code}\n" -H "X-API-Key: librekb-demo-key-2026" \
http://192.168.1.200:8139/api/v1/categories
{"categories":[{"id":1,"name":"Getting Started","slug":"getting-started","description":"First steps with the knowledge base.","icon":"book","parent_id":null,"article_count":2,"children":[{"id":4,"name":"First Steps","slug":"first-steps","description":"Nested under Getting Started.","icon":"","parent_id":1,"article_count":0,"children":[]}]},{"id":2,"name":"Troubleshooting","slug":"troubleshooting","description":"Common problems and fixes.","icon":"wrench","parent_id":null,"article_count":1,"children":[]}]}
HTTP 200
注意返回中只有 2 个顶层分类:禁用的 Draft Area(id=3)被整体省略,子分类 First Steps 挂在父节点 children 里。
api_key 也支持查询参数形式(浏览器/不便加头的客户端):
$ curl -s "http://192.168.1.200:8139/api/v1/categories?api_key=librekb-demo-key-2026" | head -c 120
{"categories":[{"id":1,"name":"Getting Started","slug":"getting-started","description":"First steps with the knowledge base.","icon
⚠️ 注意:query 形式会把密钥留在访问日志里,生产优先用
X-API-Key头。密钥超过 128 字符按未提供处理。
特殊组合:密钥留空且知识库 private 时返回 403(而非 401):
$ curl -s -w "\nHTTP %{http_code}\n" http://192.168.1.200:8139/api/v1/categories
{"error":"An API key is required when the knowledge base is private."}
HTTP 403
五、各 API 域详解
5.1 文章搜索 GET /api/v1/articles/search
参数:q(必填,≤200 字符)、page(默认 1)、per_page(默认 20,上限 50)。
$ curl -s -H "X-API-Key: librekb-demo-key-2026" \
"http://192.168.1.200:8139/api/v1/articles/search?q=password"
{"query":"password","page":1,"per_page":20,"total":2,"articles":[{"id":3,"title":"Fixing Login Issues","slug":"fixing-login-issues","category":{"id":2,"name":"Troubleshooting","slug":"troubleshooting"},"attachments":[]},{"id":1,"title":"Reset Your Password","slug":"reset-your-password","category":{"id":1,"name":"Getting Started","slug":"getting-started"},"attachments":[{"id":1,"name":"att-demo.txt","size":64,"mime":"text/plain","url":"http://192.168.1.200:8139/attachment/1"}]}]}
HTTP 200
结果不含正文(摘要形态),但带附件元数据;命中禁用文章 Unpublished Draft 与禁用分类下的文章为 0 条——可见性过滤在 SQL 层完成。
错误分支实测:
$ curl -s -H "X-API-Key: librekb-demo-key-2026" "http://192.168.1.200:8139/api/v1/articles/search?q=password&per_page=100"
{"error":"Invalid per_page."}
HTTP 400
$ curl -s -H "X-API-Key: librekb-demo-key-2026" "http://192.168.1.200:8139/api/v1/articles/search?q="
{"error":"A search query is required."}
HTTP 400
5.2 文章详情 GET /api/v1/articles/{id} 与 /articles/slug/{slug}
$ curl -s -H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/articles/1
{"article":{"id":1,"title":"Reset Your Password","slug":"reset-your-password","content":"<p>Use the <strong>Forgot password</strong> link on the login page. A reset email is sent to your address.</p>","category":{"id":1,"name":"Getting Started","slug":"getting-started"},"attachments":[{"id":1,"name":"att-demo.txt","size":64,"mime":"text/plain","url":"http://192.168.1.200:8139/attachment/1"}],"created":"2026-11-01 15:54:50","updated":"2026-11-01 15:54:50"}}
HTTP 200
$ curl -s -H "X-API-Key: librekb-demo-key-2026" \
http://192.168.1.200:8139/api/v1/articles/slug/how-to-search-the-knowledge-base
{"article":{"id":2,"title":"How to Search the Knowledge Base","slug":"how-to-search-the-knowledge-base","content":"<p>Type a query in the search box. Results match titles and content.</p>","category":{"id":1,"name":"Getting Started","slug":"getting-started"},"attachments":[],"created":"2026-11-01 15:54:50","updated":"2026-11-01 15:54:50"}}
HTTP 200
content 是前台同款清洗后 HTML;attachments[].url 为 systemURL 拼出的绝对地址(改 config.php 的 systemURL 会同步变化)。隐藏内容实测(禁用文章 id=4、禁用分类下的启用文章 id=5):
$ curl -s -H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/articles/4
{"error":"Article not found."}
HTTP 404
$ curl -s -H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/articles/5
{"error":"Article not found."}
HTTP 404
5.3 分类 GET /api/v1/categories/{id} 与 /categories/slug/{slug}
$ curl -s -H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/categories/1
{"category":{"id":1,"name":"Getting Started","slug":"getting-started","description":"First steps with the knowledge base.","icon":"book","parent_id":null,"article_count":2,"children":[{"id":4,"name":"First Steps","slug":"first-steps","description":"Nested under Getting Started.","icon":"","parent_id":1,"article_count":0,"children":[]}],"articles":[{"id":1,"title":"Reset Your Password","slug":"reset-your-password","attachments":[{"id":1,"name":"att-demo.txt","size":64,"mime":"text/plain","url":"http://192.168.1.200:8139/attachment/1"}]},{"id":2,"title":"How to Search the Knowledge Base","slug":"how-to-search-the-knowledge-base","attachments":[]}]}}
HTTP 200
$ curl -s -H "X-API-Key: librekb-demo-key-2026" \
http://192.168.1.200:8139/api/v1/categories/slug/troubleshooting
{"category":{"id":2,"name":"Troubleshooting","slug":"troubleshooting","description":"Common problems and fixes.","icon":"wrench","parent_id":null,"article_count":1,"children":[],"articles":[{"id":3,"title":"Fixing Login Issues","slug":"fixing-login-issues","attachments":[]}]}}
HTTP 200
单分类响应 = 分类节点 + 递归 children 子树 + 直属 articles 列表;article_count 为含子分类的递归计数。禁用分类(id=3)同样 404:
$ curl -s -H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/categories/3
{"error":"Category not found."}
HTTP 404
5.4 方法限制与 CORS
非 GET 方法 405 并带 Allow 头:
$ curl -s -X POST -D - -o /dev/null -H "X-API-Key: librekb-demo-key-2026" \
http://192.168.1.200:8139/api/v1/categories | grep -iE "^(HTTP|allow)"
HTTP/1.1 405 Method Not Allowed
Allow: GET, OPTIONS
$ curl -s -X POST -H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/categories
{"error":"Method not allowed."}
浏览器跨域预检 204,CORS 头组实测(api_cors_origin=* 时):
$ curl -s -X OPTIONS -D - -o /dev/null http://192.168.1.200:8139/api/v1/categories -H "Origin: http://app.example.com" | head -15
HTTP/1.1 204 No Content
Server: Apache/2.4.69 (Debian)
X-Powered-By: PHP/8.4.27
X-Frame-Options: DENY
Access-Control-Allow-Origin: *
Vary: Origin
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: X-API-Key, Content-Type
Access-Control-Max-Age: 600
Allow: GET, OPTIONS
未知 API 路径 404:
$ curl -s -H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/unknown
{"error":"Not found."}
HTTP 404
5.5 维护模式联动
后台开启 Maintenance 后 API 整体 503(优先级高于 key 校验):
$ curl -s -w "\nGET /api/v1/categories -> HTTP %{http_code}\n" \
-H "X-API-Key: librekb-demo-key-2026" http://192.168.1.200:8139/api/v1/categories
{"error":"The knowledge base is undergoing maintenance."}
GET /api/v1/categories -> HTTP 503
六、分页/过滤/排序通用约定
- 分页仅搜索端点支持:
page从 1 起(上限 1000,超限 400Invalid page.),per_page默认 20、上限 50(超限 400Invalid per_page.),非数字同样 400; - 响应携带
query/page/per_page/total元数据,客户端可用ceil(total/per_page)自行算页数; - 无过滤/排序参数:搜索按标题+内容匹配,列表按后台
order字段与 ID 排序,API 不接受覆盖; - 分类树无分页(整树返回),文章列表字段(
articles[])不分页; - 所有 ID 参数必须是 1–2147483647 的纯数字,slug 参数拒绝控制字符与超长值(400
Invalid id./Invalid slug.)。
七、常见问题
7.1 错误码速查
| 状态码 | 场景(均实测) | 响应体 |
|---|---|---|
| 200 | 正常读取 | 数据 JSON |
| 400 | per_page/page 非法、空查询、非法 id/slug | {"error":"Invalid per_page."} 等 |
| 401 | 已设 key 而缺失/错误 | {"error":"A valid API key is required."} |
| 403 | key 留空 + 知识库 private | {"error":"An API key is required when the knowledge base is private."} |
| 404 | 未知路径、隐藏/不存在的文章与分类 | {"error":"Not found."} / Article not found. / Category not found. |
| 405 | POST/PUT/DELETE 等 | {"error":"Method not allowed."} + Allow: GET, OPTIONS |
| 503 | API 开关关闭或维护模式 | {"error":"The API is disabled."} / The knowledge base is undergoing maintenance. |
7.2 内容明明存在却 404
检查三处可见性:文章 status 是否 disabled、所属分类是否 disabled、分类祖先链是否 disabled——任一命中即与前台一致地隐藏,这是设计行为而非故障。
7.3 附件 url 指向错误主机
attachments[].url 由 config.php 的 systemURL 拼接(实测值 http://192.168.1.200:8139/attachment/1)。反向代理场景把 systemURL 配成对外 HTTPS 地址即可。
7.4 想要写入 API(建文章/分类)
v2.3.3 不提供:路由层只注册 GET/OPTIONS,任何写操作 405。自动化写入只能走数据库或后台表单(带 CSRF 的脚本化 POST 实测见部署指南 4.5 节)。
7.5 前端 fetch 被 CORS 拦
后台 API 分区把 api_cors_origin 设为具体前端 origin(或 *),浏览器预检即可通过(204 + Access-Control-Allow-Headers: X-API-Key, Content-Type 实测见 5.4)。
八、参考链接
- 官方 README「Read-only API」章节与
routes.php、controllers/ApiController.php(v2.3.3 源码实查):https://github.com/michaelstaake/LibreKB - 本专栏:《LibreKB 介绍与选型指南》(https://www.quzuo.net/document/0oRDysMn1EDG)、《LibreKB 2.3.3 Docker Compose 部署指南》(https://www.quzuo.net/document/D9sq72QJvLb6)、《LibreKB 2.3.3 配置参数详解》(https://www.quzuo.net/document/0NMKA1ZoNL9x)
- 同组 API 篇参考:《LeafWiki REST API 实测》(https://www.quzuo.net/document/TYybtLemqbWQ)
- MDN CORS 文档:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS