ResourceSpace 11.0 API 使用详解
ResourceSpace 11.0 REST API 实操指南:三步签名算法(源码实证)、可复用测试客户端、get_system_status/get_resource_types/create_resource/upload_file_by_url/update_field/create_collection/delete_resource 全链路实测回显、api_upload_urls 白名单负例正例、单文件挂载 inode 陷阱与 node 存储直查等排障实录。
ResourceSpace 11.0 API 使用详解
目标:讲清 ResourceSpace 11.0 REST API 的认证签名机制、调用方法与常用端点实操。本文所有请求、响应与排障过程均在下述环境中实测完成,签名算法与关键行为均有源码依据。
一、环境信息
- 操作系统:Ubuntu 24.04.5 LTS(x86_64),宿主机 IP
192.168.1.172 - ResourceSpace:11.0(SVN
releases/11.0revision 30001),部署于 Docker Compose(应用 8132→80 端口) - API 入口:
http://192.168.1.172:8132/api/index.php - 测试账号:
admin(user ref=1)/q1w2e3r4 - $api_scramble_key:
070147602f0038f487645ac82186c134ad0adb55fd74a606de30c3e0fd8777df(安装向导生成于include/config.php) - 测试客户端:Python 3.12(仅标准库 urllib + hashlib)
二、前置条件
$enable_remote_apis = true(config.default.php 2148 行,默认开启);置 false 时仅浏览器 native 模式白名单函数可用- 知道一个具备相应权限的用户用户名及其
user ref(数据库user表ref列) $api_scramble_key(来自 config.php)- 需要上传文件的场景:在 config.php 配置
$api_upload_urls主机白名单(见 5.4)
三、API 总览
端点唯一入口 /api/index.php,通过 function 参数路由到 76 个 api_* 绑定函数(定义于 include/api_bindings.php)。常用函数分类:
| 分类 | 函数 |
|---|---|
| 系统 | get_system_status、get_resource_types |
| 资源查询 | get_resource_field_data、do_search、get_resource_path |
| 资源写入 | create_resource、update_field、upload_file_by_url、delete_resource |
| 集合 | create_collection、add_resource_to_collection、delete_resources_in_collection |
| 用户/字段管理 | create_user、create_resource_type_field 等 |
参数传递:param1、param2…(按函数签名顺序)或直接用参数名。响应为 JSON;GET 请求可加 pretty=true 美化。
四、认证与首次调用
4.1 签名算法(源码实证)
摘自 include/api_functions.php 的 get_api_key() 与 check_api_key():
function get_api_key($user) {
global $api_scramble_key;
return hash("sha256", $user . $api_scramble_key); // $user 是 user ref(ID)
}
// 校验:
$expected = hash("sha256", $userkey . $querystring);
三步签名:
api_key = sha256(user_ref + api_scramble_key)- 构造查询串(不含
sign;authmode/pretty若传则不参与签名,服务端会剔除后校验) sign = sha256(api_key + querystring)
最终 URL:/api/index.php?<querystring>&sign=<sign>,查询串中必须带 function 与 user(用户名,非 ref)。
4.2 首次调用:get_system_status
$ python3 api_test.py get_system_status
HTTP 200
{"results":{"required_php_modules":{"status":"FAIL","info":"Missing PHP modules: apcu","severity":0,"severity_text":"Critical"}},"status":"FAIL"}
实测环境镜像未装 apcu 扩展,状态检查如实报 FAIL(severity 0=Critical)——这也是部署自检的实用工具。注意返回的是系统自检状态而非调用失败。
4.3 测试客户端(可直接复用)
#!/usr/bin/env python3
# api_test.py — ResourceSpace API 签名调用客户端
import hashlib, sys, urllib.request, urllib.parse
BASE = "http://192.168.1.172:8132/api/index.php?"
USERNAME = "admin"
USER_REF = "1" # user 表 ref,非用户名
API_SCRAMBLE_KEY = "0701...(你的 $api_scramble_key)"
def call(params: dict):
params = {"user": USERNAME, **params}
qs = urllib.parse.urlencode(params)
api_key = hashlib.sha256((USER_REF + API_SCRAMBLE_KEY).encode()).hexdigest()
sign = hashlib.sha256((api_key + qs).encode()).hexdigest()
with urllib.request.urlopen(BASE + qs + "&sign=" + sign, timeout=120) as r:
print("HTTP", r.status); print(r.read().decode()[:4000])
if __name__ == "__main__":
fn = sys.argv[1]
params = {"function": fn}
for kv in sys.argv[2:]:
k, v = kv.split("=", 1); params[k] = v
call(params)
实测排障:最初版本漏传 user 参数、并误把 user ref 填进 user 字段,服务端一律返回 HTTP 401 Invalid signature。签名校验对查询串逐字符敏感,参数顺序与内容必须与签名时完全一致。
五、各 API 域详解(实测)
5.1 查询资源类型与字段
$ python3 api_test.py get_resource_types
HTTP 200
[{"ref":"1","name":"Photo",...,"resource_type_fields":["52","54","84","85","86","87"]},
{"ref":"2","name":"Document",...},{"ref":"3","name":"Video",...},{"ref":"4","name":"Audio",...}]
$ python3 api_test.py get_resource_field_data resource=2
HTTP 200
[{"resource_type_field":8,"name":"title","type":0,"value":"CLI Direct Test",...},
{"resource_type_field":18,"name":"caption","type":1,"value":"Uploaded via REST API",...}, ...]
字段 ref 与名称对照:8=title(文本)、9=extract、18=caption、51=originalfilename、73/74/75=关键词类字段。数据库直查对照:select ref,name,type from resource_type_field;
5.2 创建资源
$ python3 api_test.py create_resource resource_type=1
HTTP 200
2 # 返回新资源 ref
create_resource 签名:($resource_type, $archive = 999, $url = "", $no_exif = false, $revert = false, $autorotate = false, $metadata = "")。只建记录不传文件;archive 传 999 表示用系统默认状态。
5.3 通过 URL 上传文件(白名单机制实测)
upload_file_by_url 要求源站主机名在 $api_upload_urls 白名单内(api_validate_upload_url():未设置该变量时放行所有主机;设置为空数组则全部拒绝)。实测两步:
# 负例:config.php 中 $api_upload_urls = array();(空数组)
$ python3 api_test.py upload_file_by_url ref=2 url='http://192.168.1.172:8152/test.png'
HTTP 200
false # 直接拒绝,无错误信息
# config.php 改为 $api_upload_urls = array('192.168.1.172'); 并重建容器后
$ python3 api_test.py upload_file_by_url ref=2 url='http://192.168.1.172:8152/test.png'
HTTP 200
"2" # 成功,返回资源 ref
上传成功后文件落盘到 filestore(evenspread 目录结构):
/var/www/html/filestore/2_<hash10>/2_<hash10>.png
重要行为实测:上传会触发元数据提取流程,可能覆盖此前通过 update_field 写入的字段值(实测 PNG 无 title 元数据,先前写入的 title 被清空)。推荐顺序:create_resource → upload_file_by_url → update_field。
Docker 单文件挂载排障实录:修改 config.php 白名单时用了 sed -i,它以"写新文件+rename"方式替换文件、inode 变化,导致单文件 bind mount 失联——容器内仍是旧配置(宿主 inode 6684846 vs 容器 6946838),白名单修改不生效。验证方法:docker exec <app> grep api_upload_urls /var/www/html/include/config.php 与宿主 ls -i 对比 inode。修复:docker compose up -d --force-recreate app;预防:改宿主配置文件用 cat > file 原地写入(保 inode)或每次改完重建容器。
5.4 更新字段
$ python3 api_test.py update_field resource=2 field=18 value="Uploaded via REST API"
HTTP 200
true
field 可传 ref(8/18/51…)或字段名("title")。字段值的真实存储是 node 体系(11.0 全字段经 node):验证写入用数据库直查时查 node 与 resource_node 两表:
$ docker exec resourcespace-11.0-db mariadb -uroot -p*** --batch resourcespace \
-e "select ref,resource_type_field,name from node where resource_type_field in (8,18);
select resource,node from resource_node where resource=2"
ref resource_type_field name
283 8 CLI Direct Test
...
实测排障:核对 resource_node 时误用列名 node_ref(实际列名是 node),SQL 报错被 2>/dev/null 吞掉、输出为空,险些误判为"关联丢失"。直查时先 describe resource_node; 确认列名。
5.5 搜索与下载路径
$ python3 api_test.py do_search search="API Test" fetchrows=5
HTTP 200
[]
do_search 签名 ($search, $restypes = "", $order_by = "", $offset = 0, $fetchrows = -1, ...)。实测提示:搜索结果依赖关键词索引与后台任务状态,fetchrows 限制返回行数。本实测环境中资源新建后立即搜索返回空——关键词索引/预览生成由 batch/cron.php 驱动(docker exec resourcespace-11.0-app php /var/www/html/batch/cron.php 手动触发),生产环境配置周期 cron 后再验证。get_resource_path(resource=2&getfilepath=true)同理依赖预览文件就绪,预览未生成时返回空串。
5.6 集合操作与删除
$ python3 api_test.py create_collection name="API Test Collection" forupload=false
HTTP 200
3 # 返回集合 ref
$ python3 api_test.py add_resource_to_collection resource=2 collection=3
HTTP 200
(成功返回确认信息)
$ python3 api_test.py delete_resource resource=2
HTTP 200
true
六、分页/过滤/排序约定
- 分页:
do_search用$offset+$fetchrows;fetchrows=-1表示全部 - 排序:
order_by传字段 ref 或relevance、title、creation_date等 - 过滤:
restypes逗号分隔资源类型 ref;搜索串支持 RS 检索语法(引号短语、AND/OR) pretty=true美化 JSON,不参与签名
七、常见问题
Q1:HTTP 401 "Invalid signature"?
按频次排查:① user 参数没传或传了 user ref(必须传用户名,签名里的 api_key 才用 ref);② 参与签名的查询串与实际请求不一致(参数顺序、URL 编码差异);③ sign/authmode/pretty 被计入签名(服务端会剔除这三个,客户端不要签它们)。
Q2:upload_file_by_url 返回 false?
URL 主机不在 $api_upload_urls 白名单(空数组=全拒绝,未设置=全放行,见 5.3)。改 config.php 后记得单文件 bind mount 的 inode 陷阱(5.3 排障实录)。
Q3:update_field 返回 true 但前台/搜索没变化?
值已写入 node 体系(用 5.4 的 SQL 验证),但关键词索引与 resource 表搜索列的刷新由后台任务承担。跑一次 docker exec <app> php /var/www/html/batch/cron.php 或等待周期任务;另注意上传文件会触发元数据提取覆盖字段(5.3 行为提示),写字段放在上传之后。
Q4:哪些函数不可用?
$enable_remote_apis=false 时所有 userkey 签名调用被拒(HTTP 403 "API not enabled.");插件可能注册额外 api_* 绑定,实际可用清单以 include/api_bindings.php 与所装插件为准。
八、参考链接
- 官方 API 知识库:
https://www.resourcespace.com/knowledge-base/api/ - 官方 API 函数索引(get_system_status/do_search/create_resource 等逐函数文档):
https://www.resourcespace.com/knowledge-base/api/api_functions - 绑定函数权威来源:随版本分发的
include/api_bindings.php(本文签名以 SVNreleases/11.0revision 30001 为准) - 本专栏系列:《ResourceSpace 11.0 介绍与选型指南》、《ResourceSpace 11.0 Docker Compose 部署指南》、《ResourceSpace 11.0 配置参数详解》