XWiki 18.8 API 使用详解
XWiki 18.8.0 REST API 使用详解:/rest 端点形态与 Basic 认证、页面/附件/搜索/对象/标签全域 curl 逐条实测、multipart 附件 CSRF form_token 踩坑实录、401/403/404/405/500 错误码对照与 JVM 堆内存核验。
XWiki 18.8 API 使用详解
目标:面向需要通过 API 对 XWiki 18.8.0 做二次开发、内容批量维护与自动化运维的读者,基于已部署的 XWiki 18.8.0(Apache Tomcat 10.1.61 + PostgreSQL 16,服务端口 8148)实测环境,逐域实测 REST API 的认证方式与页面、附件、搜索、对象、标签等核心端点,沉淀真实请求与响应回显,并给出错误码对照、排障实录与 JVM 进程核验方法。本文所有请求与响应均用 curl 在下述环境逐条实测,逐字照抄。
一、环境信息
- 操作系统:Ubuntu 22.04.4 LTS
- XWiki:18.8.0(响应头
XWiki-Version: 18.8.0实测;应用服务器 Apache Tomcat 10.1.61,运行时 OpenJDK 21) - 数据库:PostgreSQL 16.15
- 部署形态:《XWiki 18.8 Docker Compose 部署指南》 完成的单机部署,服务监听 8148 端口(HTTP)
- API 形态:XWiki RESTful API(随平台内置,无需额外安装组件);默认表示格式 XML,可选 JSON
- 实测工具:curl 7.81.0(服务器本机直连
http://localhost:8148) - 实测管理员账号:
admin / q1w2e3r4
二、前置条件
2.1 服务可达与 REST 端点形态
本章确认要做的三件事:服务是否存活、REST 入口前缀是什么、认证是否可用。XWiki 18.8 的 REST API 随平台内置,路径规则是「上下文路径 + /rest + 资源路径」;本文环境按 ROOT 上下文部署(xwiki.webapppath 为空,见 《XWiki 18.8 配置参数详解》第 6.4 节),所以 REST 前缀是 /rest:
# 服务健康自检(302 重定向到首页为正常)
curl -s -o /dev/null -w '%{http_code} -> %{redirect_url}\n' http://localhost:8148/
实测回显:
302 -> http://localhost:8148/bin/view/Main/
# 带上下文前缀的 REST 入口(ROOT 部署下预期 404)
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8148/xwiki/rest/wikis
# 正确前缀
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8148/rest/wikis
实测回显:
404
200
💡 说明:若把 XWiki 以 war 包部署在
/xwiki上下文(或部署时设置CONTEXT_PATH=xwiki),REST 前缀相应变为/xwiki/rest。两种形态的资源路径部分完全相同,本文统一以实测环境的/rest书写。
2.2 认证方式
XWiki REST 支持三种认证:匿名、HTTP Basic、会话 Cookie(JSESSIONID)。运维脚本与二次开发推荐 Basic(无状态、不依赖会话)。写操作的 CSRF Token 要求有一个重要例外——multipart 附件上传必须额外携带 XWiki-Form-Token 请求头(第七章实测),其余 XML/JSON 形态的写请求 Basic 认证即可。
2.3 客户端工具
- curl(本文全部示例工具);
- jq(处理 JSON 表示时可选用);
- 浏览器可直接访问 GET 类端点查看 XML。
三、API 总览
3.1 协议与表示格式
REST 资源按 wikis → spaces → pages →(history/attachments/objects/tags/children/translations) 层级组织,HTTP 动词语义:GET 读取、PUT 创建或更新(幂等)、POST 仅用于少量子资源创建(页面对象、multipart 附件)、DELETE 删除。默认返回 XML(application/xml;charset=UTF-8),请求头带 Accept: application/json 可得 JSON(实测 application/json;charset=UTF-8)。
3.2 版本
REST 模块随平台内置发布,无独立版本端点;版本以认证后任意响应的 XWiki-Version 头为准,实测为 18.8.0(见 4.4 节响应头全文)。
3.3 认证与限流约定
- Basic 认证全程可用;错误口令的请求在应用日志留 WARN 记录(第十二章实录)。
- XWiki 默认对 REST 无内置限流(实测连续数十次请求无 429),生产环境如需限流应在反向代理层配置。
- 写操作并发:同一页面的并发写按「编辑锁 + 版本号递增」处理,REST 侧每次成功写操作都会生成一个新版本(6.5 节实测 1.1 → 8.1)。
3.4 资源与方法总览(本文全部实测)
资源端点(前缀 /rest) | 方法 | 用途 | 实测结果 |
|---|---|---|---|
/wikis | GET | 列出全部 wiki | 200 |
/wikis/{wiki} | GET | 单 wiki 资源与关联链接 | 200 |
/wikis/{wiki}/user | GET | 当前认证身份 | 200 |
/wikis/{wiki}/spaces | GET | 空间列表 | 200 |
/wikis/{wiki}/spaces/{space}/pages | GET | 空间内页面列表 | 200 |
/wikis/{wiki}/pages | GET | 全站页面列表(支持 number/start) | 200 |
/wikis/{wiki}/spaces/{space}/pages/{page} | GET/PUT/DELETE | 读 / 创建或更新 / 删除页面 | 200/201/202/204 |
/wikis/{wiki}/spaces/{space}/pages/{page}/history | GET | 页面版本历史 | 200 |
/wikis/{wiki}/spaces/{space}/pages/{page}/attachments | GET/POST | 附件列表 / multipart 上传 | 200/201 |
/wikis/{wiki}/spaces/{space}/pages/{page}/attachments/{filename} | GET/PUT/DELETE | 附件内容回读 / 原始体上传 / 删除 | 200/202/204 |
/wikis/{wiki}/spaces/{space}/pages/{page}/objects | GET/POST | 对象列表 / 新建对象 | 200/201 |
/wikis/{wiki}/spaces/{space}/pages/{page}/tags | GET/PUT | 读取 / 覆盖设置标签 | 200/202 |
/wikis/{wiki}/search?query= | GET | 站内搜索 | 200 |
/wikis/{wiki}/query?q= | GET | 旧查询插件入口 | 200(本环境返回空集合) |
/wikis/{wiki}/classes | GET | 类定义列表 | 200 |
/wikis/{wiki}/classes/{class} | GET | 单个类及其属性 | 200 |
/wikis/{wiki}/modifications | GET | 全站最近修改 | 200 |
⚠️ 注意:页面子资源的正确路径形态是
/spaces/{space}/pages/{page}。旧文档常见的/pages/{space}/{page}写法不匹配资源模板,会得到 Tomcat 层 404(6.2 节实录)。
四、认证与首次调用
4.1 健康检查
见 2.1 节:/ 返回 302 跳转 /bin/view/Main/,/rest/wikis 返回 200 即为就绪。巡检脚本建议以这两个状态码做探活。
4.2 匿名身份探测
不带认证调用身份端点,确认当前匿名身份:
curl -s http://localhost:8148/rest/wikis/xwiki/user
实测回显:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><user xmlns:xwiki="http://www.xwiki.org" xmlns="http://www.xwiki.org/user"><id>XWiki.XWikiGuest</id><firstName>Guest</firstName><avatarUrl>http://localhost:8148/resources/icons/xwiki/noavatar.png?cache-version=1790683364000</avatarUrl><global>true</global><displayName>Unknown User</displayName><company></company><about></company><email></email><phone></phone><address></address><blog></blog><blogFeed></blogFeed></user>
(displayName 等空标签为原始回显保留。)匿名身份为 XWiki.XWikiGuest,只读公开内容。
4.3 Basic 认证身份
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/user
实测回显:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><user xmlns:xwiki="http://www.xwiki.org" xmlns="http://www.xwiki.org/user"><xwiki:link href="http://localhost:8148/rest/wikis/xwiki/spaces/XWiki/pages/admin" rel="http://www.xwiki.org/rel/page"/><xwiki:link href="http://localhost:8148/rest/wikis/xwiki/spaces/XWiki/pages/admin/history" rel="http://www.xwiki.org/rel/history"/><id>xwiki:XWiki.admin</id><avatarUrl>http://localhost:8148/resources/icons/xwiki/noavatar.png?cache-version=1790683364000</avatarUrl><global>true</global><xwikiRelativeUrl>http://localhost:8148/bin/view/XWiki/admin</xwikiRelativeUrl><xwikiAbsoluteUrl>http://localhost:8148/bin/view/XWiki/admin</xwikiAbsoluteUrl><displayName>admin</displayName><company></company><about></company><email>admin@example.com</email><phone></phone><address></address><blog></blog><blogFeed></blogFeed></user>
身份变为 xwiki:XWiki.admin(前缀为 wiki 名),说明 Basic 认证生效。
4.4 响应头速览(含版本与 form_token)
curl -sI -u admin:q1w2e3r4 http://localhost:8148/rest/wikis
实测回显:
HTTP/1.1 200
Content-Script-Type: text/javascript
Set-Cookie: JSESSIONID=6A80DE1D3C9B8F6976204003E5CBD616; Path=/; HttpOnly
XWiki-User: xwiki:XWiki.admin
XWiki-Version: 18.8.0
XWiki-Form-Token: 0Whzdzxgi7QenAAicoebIw
Content-Type: application/xml;charset=UTF-8
Content-Language: en
Content-Length: 738
Date: Thu, 22 Oct 2026 02:05:06 GMT
逐头解读:XWiki-User 确认认证身份;XWiki-Version 是版本取证依据;XWiki-Form-Token 是会话级 CSRF 令牌——multipart 附件上传必须原样带回(第七章);Set-Cookie 的 JSESSIONID 如需会话方式调用可自行维护,脚本场景用 Basic 可忽略。
五、Wiki 与空间域
5.1 wiki 列表
curl -s http://localhost:8148/rest/wikis
实测回显(匿名可读):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><wikis xmlns="http://www.xwiki.org"><link href="http://localhost:8148/rest/wikis/query" rel="http://www.xwiki.org/rel/query"/><wiki><link href="http://localhost:8148/rest/wikis/xwiki/spaces" rel="http://www.xwiki.org/rel/spaces"/><link href="http://localhost:8148/rest/wikis/xwiki/classes" rel="http://www.xwiki.org/rel/classes"/><link href="http://localhost:8148/rest/wikis/xwiki/modifications" rel="http://www.xwiki.org/rel/modifications"/><link href="http://localhost:8148/rest/wikis/xwiki/search" rel="http://www.xwiki.org/rel/search"/><link href="http://localhost:8148/rest/wikis/xwiki/query" rel="http://www.xwiki.org/rel/query"/><id>xwiki</id><name>xwiki</name></wiki></wikis>
只有一个主 wiki xwiki(farmfarm 模式未启子 wiki 时属正常)。<link rel=.../> 是超媒体链接,指明该 wiki 下可继续访问的子资源,可直接顺着调用。
5.2 单 wiki 资源
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki
实测回显(节选,保留起始结构):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><wiki xmlns="http://www.xwiki.org"><link href="http://localhost:8148/rest/wikis/xwiki/spaces" rel="http://www.xwiki.org/rel/spaces"/><link href="http://localhost:8148/rest/wikis/xwiki/classes" rel="http://www.xwiki.org/rel/classes"/><link href="http://localhost:8148/rest/wikis/xwiki/modifications" rel="http://www.xwiki.org/rel/modifications"/>
(输出为节选,后续为描述与更多链接元素。)
5.3 空间列表
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/spaces
实测回显(节选,第一个空间):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><spaces xmlns="http://www.xwiki.org"><space><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Help/pages" rel="http://www.xwiki.org/rel/pages"/><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Help/pages/WebHome" rel="http://www.xwiki.org/rel/home"/><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Help/search" rel="http://www.xwiki.org/rel/search"/><id>xwiki:Help</id><wiki>xwiki</wiki><name>Help</name><home>xwiki:Help.WebHome</home><xwikiRelativeUrl>http://localhost:8148/bin/view/Help/</xwikiRelativeUrl>
(输出为节选;标准 flavor 自带 Help、Sandbox、Main、XWiki 等空间,后续元素以此类推。)
5.4 空间内页面列表
curl -s -u admin:q1w2e3r4 'http://localhost:8148/rest/wikis/xwiki/spaces/Help/pages?number=2'
实测回显(节选,number=2 只返回 2 条):
<pageSummary><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Help/pages/WebHome" rel="http://www.xwiki.org/rel/page"/><id>xwiki:Help.WebHome</id><fullName>Help.WebHome</fullName><wiki>xwiki</wiki><space>Help</space><name>WebHome</name>
(输出为节选;fullName 命名规则为 空间.页面名。)
六、页面域
6.1 全站页面列表与分页
分页参数是 number(页大小)与 start(起始下标)。实测 number=2&start=0 恰好返回 2 条:
curl -s -u admin:q1w2e3r4 'http://localhost:8148/rest/wikis/xwiki/pages?number=2&start=0' | grep -o '<pageSummary>' | wc -l
实测回显:
2
⚠️ 注意:
limit/offset参数不生效(实测?limit=3返回了远超 3 条的完整列表)。XWiki REST 的分页参数固定为number/start,写脚本时不要套用其他产品的习惯参数。
6.2 获取单页
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/spaces/Main/pages/WebHome
实测回显(节选,保留主要字段):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><page xmlns="http://www.xwiki.org"><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Main/pages/WebHome/history" rel="http://www.xwiki.org/rel/history"/><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Main/pages/WebHome/children" rel="http://www.xwiki.org/rel/children"/><link href="http://localhost:8148/rest/syntaxes" rel="http://www.xwiki.org/rel/syntaxes"/><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Main/pages/WebHome" rel="self"/><id>xwiki:Main.WebHome</id><fullName>Main.WebHome</fullName><wiki>xwiki</wiki><space>Main</space><name>WebHome</name><title>Home</title><rawTitle>Home</rawTitle><parent></parent><parentId></parentId><version>1.1</version><author>XWiki.Admin</author><xwikiRelativeUrl>http://localhost:8148/bin/view/Main/</xwikiRelativeUrl><xwikiAbsoluteUrl>http://localhost:8148/bin/view/Main/</xwikiAbsoluteUrl><translations default="en"><translation language="en"><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Main/pages/WebHome" rel="http://www.xwiki.org/rel/page"/><link href="http://localhost:8148/rest/wikis/xwiki/spaces/Main/pages/WebHome/history" rel="http://www.xwiki.org/rel/history"/></translation>
(输出为节选;后续为 no/de 等语言翻译变体链接与正文 content 元素。)字段解读:version 当前版本、author 最后编辑者、translations 列出该页全部语言变体;content 元素为 XWiki/2.1 语法源码,批量迁移内容时读它即可。
⚠️ 注意:
/rest/wikis/xwiki/pages/Main/WebHome这种「page 集合下拼路径」的旧写法返回 Tomcat 404 错误页(实测 HTML 错误页,Apache Tomcat/10.1.61页脚),务必使用/spaces/{space}/pages/{page}形态。
JSON 表示(Accept: application/json):
curl -s -u admin:q1w2e3r4 -H 'Accept: application/json' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage \
-o /dev/null -w '%{http_code} %{content_type}\n'
实测回显:
200 application/json;charset=UTF-8
JSON 响应体(节选,起始 300 字节):
{"links":[{"href":"http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox","rel":"http://www.xwiki.org/rel/space","type":null,"hrefLang":null},{"href":"http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/history","rel":"http://www.xwiki.org/rel/history","type":null,"hrefLang":null
(输出为节选。同一资源在 XML/JSON 两种表示间字段名一致,按 Accept 头切换即可。)
6.3 创建页面(PUT)
PUT 对不存在的页面即创建,返回 201:
curl -s -u admin:q1w2e3r4 -X PUT -H 'Content-Type: application/xml' \
-d '<?xml version="1.0" encoding="UTF-8"?><page xmlns="http://www.xwiki.org"><title>API 测试页</title><content>这是 REST API 创建的测试内容 v1。</content></page>' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage \
-o /dev/null -w '%{http_code}\n'
实测回显:
201
创建后立即 GET 可见(实测回显节选):
<id>xwiki:Sandbox.APITestPage</id><fullName>Sandbox.APITestPage</fullName><wiki>xwiki</wiki><space>Sandbox</space><name>APITestPage</name><title>API 测试页</title><rawTitle>API 测试页</rawTitle><parent></parent><parentId></parentId><version>1.1</version><author
中文标题与中文正文均正常落库,无需额外编码处理。
6.4 更新页面内容(PUT)
PUT 对已存在页面即更新,返回 202(Accepted,异步落库语义):
curl -s -u admin:q1w2e3r4 -X PUT -H 'Content-Type: application/xml' \
-d '<?xml version="1.0" encoding="UTF-8"?><page xmlns="http://www.xwiki.org"><content>这是 REST API 更新后的内容 v2。</content></page>' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage \
-o /dev/null -w '%{http_code}\n'
实测回显:
202
回读确认版本与内容(实测回显):
<version>2.1
<author>XWiki.admin
<content>这是 REST API 更新后的内容 v2。
版本号从 1.1 推进到 2.1(大版本.小版本,小版本在 REST 内容更新下同样递增),编辑者为认证账号。> 💡 说明:PUT 请求体中未给出的字段(如 title)保持原值不变,按需给出字段即可增量更新。
6.5 页面版本历史
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/history | grep -o '<version>[^<]*'
实测回显(该测试页删除前,历经创建、两次内容更新、两次标签 PUT、评论对象 POST、两次附件写后共 8 个版本):
<version>8.1
<version>7.1
<version>6.1
<version>5.1
<version>4.1
<version>3.1
<version>2.1
<version>1.1
结论:REST 的每一类写操作(内容、标签、对象、附件)都会生成新版本,做批量脚本前要预估版本膨胀;版本清理策略见 《XWiki 18.8 配置参数详解》第 6.3 节的存储域参数。
6.6 POST 不能建页(405 实测)
XWiki REST 没有提供 POST 建页方法,对集合资源 POST 会得到 405:
curl -s -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/xml' \
-d '<page xmlns="http://www.xwiki.org"><name>APITestPage</name></page>' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages \
-o /dev/null -w '%{http_code}\n'
curl -s -u admin:q1w2e3r4 -X POST http://localhost:8148/rest/wikis/xwiki/pages -o /dev/null -w '%{http_code}\n'
实测回显:
405
405
💡 说明:这与 MediaWiki 的
action=editAPI 习惯不同——XWiki 一律用 PUT 在目标 URL 上「创建或更新」,天然幂等,重复执行不会产生重复页面,脚本重试更安全。
6.7 删除页面
curl -s -u admin:q1w2e3r4 -X DELETE -o /dev/null -w '%{http_code}\n' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage
curl -s -u admin:q1w2e3r4 -o /dev/null -w '%{http_code}\n' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage
实测回显:
204
404
DELETE 返回 204,再次 GET 即 404,删除即时生效。被删页面进入站内回收站(18.8 默认开启),恢复只能从 Web 界面回收站操作,REST 未提供恢复端点——批量删除前建议先导出页面 XML 备份。
七、附件域
7.1 multipart 上传:403 踩坑与 form_token 解法
先用最直觉的方式上传(multipart 表单 + Basic 认证):
curl -s -u admin:q1w2e3r4 -X POST -F 'file=@/opt/data/quzuo/tmp/xwiki/api-test-note.txt' \
-w '%{http_code}\n' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/attachments
实测回显:
403
排查应用日志 /usr/local/xwiki/data/logs/xwiki.log,找到对应的 WARN(逐字照抄):
2026-10-22 02:08:01,838 [http-nio-8080-exec-2 - http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/attachments] WARN o.x.c.i.DefaultCSRFToken - Secret CSRF token verification failed (token: [null], stored token: [0Whzdzxgi7QenAAicoebIw])
根因:multipart/form-data 形态的请求即使走 Basic 认证,也会被 CSRF 过滤器拦截(过滤器对表单类请求强制校验 form_token,而该请求未携带)。把 4.4 节响应头里的 XWiki-Form-Token 值原样放进请求头重试:
TOKEN=$(curl -sI -u admin:q1w2e3r4 http://localhost:8148/rest/wikis | grep -i 'XWiki-Form-Token' | tr -d '\r' | awk '{print $2}')
curl -s -u admin:q1w2e3r4 -X POST -F 'file=@/opt/data/quzuo/tmp/xwiki/api-test-note.txt' \
-H "XWiki-Form-Token: $TOKEN" \
-w '%{http_code}\n' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/attachments
实测回显:
201
⚠️ 注意:实测把
form_token放在 URL 查询串或表单字段里都仍然 403(日志同样报token: [null],REST 服务侧不解析这两种形态),只有请求头XWiki-Form-Token有效。token 是会话级值,脚本每次运行时先发一个认证请求取头即可,无需手工维护。
7.2 原始体上传(PUT,推荐脚本使用)
REST 同时提供非 multipart 的附件写法:对附件 URL 直接 PUT 原始字节流,免 token、免 multipart 组包:
curl -s -u admin:q1w2e3r4 -X PUT -H 'Content-Type: text/plain' \
--data-binary @/opt/data/quzuo/tmp/xwiki/api-test-note.txt \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/attachments/api-test-note.txt
实测回显(节选):
<id>xwiki:Sandbox.APITestPage@api-test-note.txt
<name>api-test-note.txt
<size>51
<version>1.2
<mimeType>text/plain
<author>XWiki.admin
(输出为节选;此时 7.1 的 POST 已建出版本 1.1,PUT 同名附件即覆盖,版本推进到 1.2,返回 202。)对同一附件重复 PUT 生成新版本,与页面版本化规则一致。
7.3 附件列表、内容回读与删除
# 列表
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/attachments | grep -o '<\(filename\|mimeType\|size\|version\)>[^<]*'
# 内容回读(字节级一致)
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/attachments/api-test-note.txt
# 删除
curl -s -u admin:q1w2e3r4 -X DELETE -o /dev/null -w '%{http_code}\n' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/attachments/api-test-note.txt
实测回显(三段依序):
<size>51
<version>1.2
<mimeType>text/plain
XWiki REST API 附件上传实测内容 2026-10-22
204
删除后再次请求附件列表返回空集合,内容回读 404,闭环成立。
八、搜索与查询域
8.1 站内搜索 /search
curl -s -u admin:q1w2e3r4 'http://localhost:8148/rest/wikis/xwiki/search?query=REST%20API' | grep -o '<\(searchResult\|id\|pageFullName\)>[^<]*'
实测回显(节选):
<searchResult>
<id>xwiki:Sandbox.APITestPage
<pageFullName>Sandbox.APITestPage
<searchResult>
<id>xwiki:Main.ConfigParamTest
<pageFullName>Main.ConfigParamTest
(grep 按 < 截断元素,行尾为原文缺失部分;命中两条结果:REST 创建的测试页与此前配置实验页。)query 参数做全文检索,命中标题与正文;number/start 同样可用于结果分页。
8.2 旧查询入口 /query(本环境返回空集合实录)
/rest/wikis/{wiki}/query?q= 是旧查询插件(Query Plugin/Query Manager)的 REST 入口。响应自带调用模板,但实测三种常用语法均返回空结果集合:
curl -s -u admin:q1w2e3r4 'http://localhost:8148/rest/wikis/xwiki/query?q=title%3AHome'
实测回显(逐字,全文即为一个空集合 + 模板声明):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><searchResults xmlns="http://www.xwiki.org" template="http://localhost:8148/rest/wikis/xwiki/query?q={query}(&type={type})(&number={number})(&start={start})(&orderField={fieldname}(&order={asc|desc}))(&distinct=1)(&prettyNames={false|true})(&wikis={wikis})(&className={classname})"/>
q=name:APITestPage、q=object:XWiki.XWikiUsers 两种语法实测同样返回该空集合。结论:该端点依赖的旧查询插件在标准 flavor 的 18.8.0 中无可用查询实现,生产脚本一律使用 8.1 节的 /search;如需结构化查询(按对象属性、按类过滤),用页面遍历 + 对象端点组合,或在服务器端用 XWQL 页面(/bin/view/... 中嵌 services.query 脚本)实现。模板中展示的 number/start/orderField/order/distinct/prettyNames/wikis/className 参数仅在查询实现可用时生效。
九、对象与类域
9.1 页面对象列表
XWiki 的结构化数据挂在页面的「对象」(XObject)上。先看用户页自带的对象:
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/spaces/XWiki/pages/admin/objects | grep -o '<\(className\|number\)>[^<]*'
实测回显(节选):
<className>XWiki.XWikiRights
<className>XWiki.XWikiUsers
(输出为节选;<number> 为同类对象的序号,从 0 起。)
9.2 新建对象(以评论为例)
curl -s -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/xml' \
-d '<?xml version="1.0" encoding="UTF-8"?><object xmlns="http://www.xwiki.org"><className>XWiki.XWikiComments</className><property name="author">admin</property><property name="comment">来自 REST API 的评论实测</property></object>' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/objects \
-o /dev/null -w '%{http_code}\n'
实测回显:
201
建后核验对象列表(实测回显):
<className>XWiki.TagClass
<number>0
<className>XWiki.XWikiComments
<number>0
XWikiComments 对象编号 0 已挂到页面,属性值随对象保存。对象 POST 同时会推动页面版本号(见 6.5 节版本清单)。
9.3 类定义列表与单个类
curl -s -u admin:q1w2e3r4 'http://localhost:8148/rest/wikis/xwiki/classes?number=4' | grep -o '<id>[^<]*'
实测回显:
<id>AnnotationCode.AnnotationClass
<id>AnnotationCode.AnnotationConfig
<id>AppWithinMinutes.Boolean
<id>AppWithinMinutes.Content
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/classes/XWiki.XWikiUsers
实测回显(节选):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><class xmlns="http://www.xwiki.org"><link href="http://localhost:8148/rest/wikis/xwiki/classes/XWiki.XWikiUsers" rel="self"/><link href="http://localhost:8148/rest/wikis/xwiki/classes/XWiki.XWikiUsers/properties" rel="http://www.xwiki.org/rel/properties"/><link href="http://localhost:8148/rest/wikis/xwiki/classes/XWiki.XWikiUsers/objects" rel="http://www.xwiki.org/rel/objects"/><id>XWiki.XWikiUsers</id><name>XWiki.XWikiUsers</name><property name="first_name" type="String"><link href="http://localhost:8148/rest/wikis/xwiki/classes/XWiki.XWikiUsers/properties/first_name" rel="self"/><attribute name="name" value="first_name"/>
(输出为节选;property 元素带 name/type 属性,属性细节在 attribute 子元素中。)基于类清单 + 对象端点可做「按类批量导出页面」的盘点脚本。
十、标签域
10.1 正确写法:标签名是属性不是文本
curl -s -u admin:q1w2e3r4 -X PUT -H 'Content-Type: application/xml' \
-d '<?xml version="1.0" encoding="UTF-8"?><tags xmlns="http://www.xwiki.org"><tag name="apitest"/><tag name="restapi"/></tags>' \
http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/tags \
-o /dev/null -w '%{http_code}\n'
curl -s -u admin:q1w2e3r4 http://localhost:8148/rest/wikis/xwiki/spaces/Sandbox/pages/APITestPage/tags | grep -o '<tag name="[^"]*"'
实测回显:
202
<tag name="apitest"
<tag name="restapi"
(grep 按 < 截断," 后的原文尖括号未显示。)PUT 是整体覆盖语义:新请求里没列出的旧标签会被移除。
10.2 踩坑实录:文本子元素形态得到空标签
第一次尝试把标签名写成文本子元素 <tag>apitest</tag>,PUT 返回 202 但 GET 显示空标签(实测回显逐字):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><tags xmlns="http://www.xwiki.org"><tag name=""><link href="http://localhost:8148/rest/wikis/xwiki/tags/" rel="http://www.xwiki.org/rel/tag"/></tag><tag name=""><link href="http://localhost:8148/rest/wikis/xwiki/tags/" rel="http://www.xwiki.org/rel/tag"/></tag></tags>
服务端解析的是 name 属性,文本内容被忽略,结果给页面挂了两个空名标签(对应两个 XWiki.TagClass 对象)。解法即 10.1 的属性写法,PUT 覆盖后空标签随之清除。全局标签清单用 GET /rest/wikis/xwiki/tags 查看。
十一、分页、过滤与排序通用约定
- 分页:全部集合类端点统一用
number(页大小)+start(起始下标),实测有效(6.1 节恰好 2 条);limit/offset无效。 - 排序:集合端点本身不提供排序参数;排序诉求走 8.2 节模板中的
orderField/order(仅在查询实现可用时生效),或脚本取回后本地排序。 - 过滤:列表端点按资源天然过滤(空间级列表只含本空间);按类/按属性过滤无 REST 参数,用遍历 + 对象端点组合实现。
- 表示格式:
Accept: application/json与 XML 任选,字段语义一致(6.2 节实测两种形态)。 - 超媒体链接:每个资源响应的
<link rel=.../>列出合法的后续操作地址,按链接走即可避免拼错路径(如/pages/{space}/{page}的 404 坑)。
十二、错误码对照与排障实录
以下状态码全部实测复现,响应体与日志逐字保留:
| 状态码 | 触发场景(实测) | 响应/日志特征 | 处理 |
|---|---|---|---|
| 401 | 口令错误或匿名执行写操作(带合法请求体) | 响应体含 com.xpn.xwiki.XWikiException: Error number 9001 in 9: Access denied in edit mode on document xwiki:Sandbox.CreateByPut;日志 WARN Authentication failure with login [admin] | 核对凭据;匿名写被权限拒绝属预期 |
| 403 | multipart 附件上传未带 CSRF 令牌 | 日志 WARN Secret CSRF token verification failed (token: [null], stored token: [...]) | 请求头携带 XWiki-Form-Token(7.1 节) |
| 404 | 页面不存在;或页面路径写成了 /pages/{space}/{page} 旧形态 | Tomcat HTML 错误页(非 XML) | 核对资源路径形态 |
| 405 | 对页面集合 POST 建页 | 空 | XWiki 无 POST 建页,改用 PUT(6.6 节) |
| 500 | PUT/POST 带空请求体 | Tomcat 异常报告,根因 NullPointerException: Cannot invoke "org.xwiki.rest.model.jaxb.Page.getContent()" because "rest..." | 请求体必须携带合法的 XML/JSON 表示(哪怕只写一个字段) |
⚠️ 注意:500 与 401 容易混淆——空请求体(无认证头或有认证头)都会 500,只有「带合法表示体 + 凭据错误/无权限」才是 401。写脚本时先确保请求体非空,再排查认证。
排障取证统一看应用日志(固定落盘路径,-f 跟踪):
tail -f /usr/local/xwiki/data/logs/xwiki.log
实测该日志按行包含时间戳、线程、请求 URL、级别与消息(7.1、十二章引用的 WARN 均出自此文件)。
十三、服务进程与 JVM 内存核验
XWiki 是 Java 进程(Tomcat 内核),API 排障前先确认进程与堆参数真实生效。进程查看与生效值实测:
ps -ef | grep [j]ava
实测回显(节选,保留关键 JVM 参数,整行较长):
root 1 0 Oct21 ? 00:02:39 /opt/java/openjdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xms512m -Xmx1024m -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true -Dorg.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH=true -Djava.security.egd=file:/dev/./urandom -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dsun.io.useCanonCaches=false -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/xwiki/data/tmp org.apache.catalina.startup.Bootstrap start
逐项解读:实测堆参数为 -Xms512m -Xmx1024m(初始堆 512MB、最大堆 1GB);-Djava.io.tmpdir=/usr/local/xwiki/data/tmp 指向永久目录下的临时目录;--add-opens 系列为 OpenJDK 21 反射开放参数,属官方启动脚本固定值,勿动。
堆内存的修改位置:JAVA_OPTS 在部署时的服务环境变量中设置——本文环境按 《XWiki 18.8 Docker Compose 部署指南》部署,改 compose 编排中 web 服务的 JAVA_OPTS 环境变量后重启服务生效;以 systemd 托管的裸机部署则写在服务单元的 Environment="JAVA_OPTS=-Xms512m -Xmx1024m"(每行仅一个 Environment 项)。修改后用上面的 ps -ef | grep [j]ava 复核 -Xms/-Xmx 实际生效值(本环境 jps 命令不可用,以 ps -ef 为准)。
生产建议(结合官方镜像默认值与中小规模实测):API 批量场景并发不高,但渲染与索引吃堆,-Xmx 不建议低于 1GB;扩容到 4GB 物理内存以上时可按 -Xms1g -Xmx3g 档位设置(配置模板见 《XWiki 18.8 配置参数详解》第十章)。
十四、常见问题
Q1:REST 前缀到底带不带 /xwiki?
取决于部署上下文。ROOT 上下文部署(本文环境)前缀是 /rest,带 /xwiki/rest 会 404;war 部署到 /xwiki 上下文则前缀为 /xwiki/rest。判定方法:curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8148/xwiki/rest/wikis,404 即说明当前是 ROOT 形态(2.1 节实录)。
Q2:上传附件 403,但同一账号改页面是 202?
multipart 附件上传被 CSRF 过滤器拦截,Basic 认证不豁免该类请求。解法:从任意认证后响应头取 XWiki-Form-Token 放入请求头重试;或改用 7.2 节的 PUT 原始体上传,完全绕开 multipart(推荐脚本使用)。
Q3:写请求返回 500,日志却有认证失败 WARN,是不是认证问题?
不是。实测「空请求体」无论认证与否都返回 500(根因是服务端解析空表示时代码空指针);「带合法 XML 体 + 错误口令」才是 401。先给请求体补上 <page xmlns="http://www.xwiki.org">... 最外层元素再排查。
Q4:页面列表翻页参数不生效?
参数名是 number/start,不是 limit/offset(6.1 节实测对照)。
Q5:设置了标签但名字是空的?
标签名必须写 name 属性(<tag name="apitest"/>),写成文本子元素会被忽略并产生空名标签(10.2 节实录);用属性写法重新 PUT 覆盖即可清除。
Q6:/query 端点为什么什么都查不到?
该端点依赖旧查询插件,在标准 flavor 的 18.8.0 中实测三种语法均返回空集合(8.2 节实录)。内容检索用 /search;结构化盘点用类 + 对象端点组合。
Q7:DELETE 删掉的页面能从 REST 恢复吗? 不能。REST 只有删除端点,恢复需到 Web 界面的回收站操作(18.8 默认开启回收站,行为受 《XWiki 18.8 配置参数详解》第 6.3 节存储域参数控制)。批量删除前建议逐页 GET 导出 XML 备份。
Q8:如何做每日巡检?
三步探活(实测命令):curl -s -o /dev/null -w '%{http_code}' http://localhost:8148/ 应为 302;curl -s -o /dev/null -w '%{http_code}' -u admin:q1w2e3r4 http://localhost:8148/rest/wikis 应为 200;ps -ef | grep [j]ava 确认进程与 -Xmx 未漂移。异常时看 /usr/local/xwiki/data/logs/xwiki.log 末段。
十五、参考链接
- 官方 REST API 文档:https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/REST
- 官方管理员指南:https://www.xwiki.org/xwiki/bin/view/Documentation/AdminGuide/
- REST 模块源码(xwiki-platform 仓库 xwiki-platform-rest-server 模块):https://github.com/xwiki/xwiki-platform
- 部署与环境变量(JAVA_OPTS 所在位置)上下文:《XWiki 18.8 Docker Compose 部署指南》
- 配置参数与存储/版本化策略:《XWiki 18.8 配置参数详解》
- 选型与版本背景:《XWiki 介绍与选型指南》