DSpace REST API 使用详解(HAL 超媒体认证、CRUD 与检索实测)

DSpace REST API 使用详解:10.1 后端逐条 curl 实测——HAL 根资源即契约(v3/api-docs 404 取证)、XSRF+JWT 双要素认证(403/401 负向实录、30 分钟 TTL 解码验证)、社区/收藏全生命周期 CRUD(201/200/PATCH/405/204/404)、dso/find 302 寻址与 discover 全文检索、eperson 隐私边界与 19 项方法-端点状态码速查。

DSpaceDSpaceREST APIJava知识库2026/11/01

DSpace REST API 使用详解(HAL 超媒体认证、CRUD 与检索实测)

目标:以 2026-11-01 实测的 DSpace 10.1 REST 后端(入口 http://192.168.1.200:8139/server,部署见《DSpace 10.1 Docker Compose 部署指南》)为对象,逐条 curl 实测 HAL 超媒体 API 的契约发现、XSRF+JWT 认证、社区/收藏全生命周期 CRUD 与三类检索,所有请求与回显均为实录。配置项对 API 回显的影响见《DSpace 配置参数详解(env/local.cfg/dspace.cfg 三层优先级链与关键项)》。

一、环境信息与 API 契约取证

项目内容
实测日期2026-11-01
API 基址http://192.168.1.200:8139/server/api(本机 curl 走 127.0.0.1:8139)
版本DSpace 10.1(根资源 dspaceVersion 实测回显)
媒体类型application/hal+json;charset=UTF-8(响应头实测)
认证方式表单登录换 JWT Bearer + XSRF 双要素(第三、四节实测)
示例账号admin@dspace.local / q1w2e3r4(部署篇第六节创建)

先给一个关键取证结论——10.1 不发布 OpenAPI 契约。常规机器可读契约端点实测 404:

$ curl -s http://127.0.0.1:8139/server/v3/api-docs
{"type":"about:blank","title":"Not Found","status":404,"detail":"No static resource v3/api-docs.","instance":"/server/v3/api-docs"}

DSpace REST 的契约就是 HAL 超媒体本身:从根资源出发,沿 _links 逐层发现端点与 URI 模板(第二节)。写集成代码前不需要抓包猜路径,curl /server/api 即是"接口文档"。

二、HAL 根资源:接口即文档

$ curl -s http://127.0.0.1:8139/server/api
{
  "dspaceUI" : "http://192.168.1.200:8139",
  "dspaceName" : "DSpace via local.cfg",
  "dspaceServer" : "http://192.168.1.200:8139/server",
  "dspaceVersion" : "DSpace 10.1",
  "type" : "root",
  "uniqueType" : "root.root",
  "_links" : {
    "actuator" : { "href" : "http://192.168.1.200:8139/server/actuator" },
    "authn" : { "href" : "http://192.168.1.200:8139/server/api/authn" },
    "communities" : { ... },
    ...
  }
}

三个实测要点:

  1. 站点标识来自配置链:dspaceName 显示 local.cfg 生效值、dspaceServer/dspaceUI 显示环境变量覆写值——API 根是验证配置是否生效的最快探针(配置篇第二、五节方法)。
  2. 顶层链接共 85 个(实测 _links 键计数),覆盖全部对象域:communities、collections、items、bitstreams、bundles、epersons、groups、resourcepolicies、workflowitems、workspaceitems、pooltasks、versions、discover、statistics、oai 相关走 pid/vocabularies 等;管理域 scripts、features、securitysettings、systemwidealerts 等。
  3. 链接带 URI 模板,如 DSO 全局寻址链接原文:
"dso" : { "href" : "http://192.168.1.200:8139/server/api/dso/find{?uuid}" }

{?uuid} 是 RFC 6570 模板占位,拼 ?uuid=<对象ID> 即得查询(第五节实测)。

三、认证实测:XSRF 双要素 + JWT

DSpace 的写操作保护采用"Cookie + 同名请求头"双要素(CSRF 防护),登录本身也要求先取得 XSRF 上下文。完整流程三步:

$ curl -s -c dscj.txt http://127.0.0.1:8139/server/api/authn/status -o /dev/null
$ grep DSPACE-XSRF dscj.txt
#HttpOnly_127.0.0.1	FALSE	/server	FALSE	0	DSPACE-XSRF-COOKIE	30ce2ecb-657c-4910-a90d-cc538cd083b6

匿名状态查询确认当前未登录:

$ curl -s http://127.0.0.1:8139/server/api/authn/status
{ "type": "status", "authenticated": false, ... }

3.2 表单登录换 JWT(负向对照先测)

不带 XSRF 头直接登录——凭据正确也拒绝,实测 403:

$ curl -s -X POST http://127.0.0.1:8139/server/api/authn/login \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'user=admin@dspace.local&password=q1w2e3r4'
{"timestamp":"2026-11-01T14:12:17.123+00:00","status":403,"error":"Forbidden","message":"Access is denied. Invalid CSRF token.","path":"/server/api/authn/login"}

带 Cookie + 同名头登录,实测 200,JWT 在响应头 Authorization 里返回,响应体为空(Content-Length: 0):

$ curl -s -b dscj.txt -X POST http://127.0.0.1:8139/server/api/authn/login \
    -H "X-XSRF-TOKEN: 30ce2ecb-657c-4910-a90d-cc538cd083b6" \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'user=admin@dspace.local&password=q1w2e3r4' -D -
HTTP/1.1 200
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJlaWQiOiJiYzAyM2RhZi01OWE1LTRiMjItOTc0Yi1kNmJlYjhhZjNmMGQiLCJzZyI6W10sImV4cCI6MTc5MzU0NDEzNywiYXV0aGVudGljYXRpb25NZXRob2QiOiJwYXNzd29yZCJ9....

解出 payload 实录:

{"eid":"bc023daf-59a5-4b22-974b-d6beb8af3f0d","sg":[],"exp":1793544137,"authenticationMethod":"password"}

exp 与登录时刻(22:12:17)相差 1800 秒——令牌有效期 30 分钟(默认 authentication.token.validity=1800)。过期后持旧令牌请求实测 HTTP 401,重新登录即可(长任务集成需自行刷新)。

3.3 持令牌验证身份

$ curl -s -H "Authorization: Bearer <token>" http://127.0.0.1:8139/server/api/authn/status
{
    "id": null,
    "okay": true,
    "authenticated": true,
    "authenticationMethod": "password",
    "type": "status",
    "uniqueType": "authn.status",
    "_links": {
        "eperson": { "href": ".../server/api/eperson/epersons/bc023daf-59a5-4b22-974b-d6beb8af3f0d" },
        "specialGroups": { "href": ".../server/api/authn/status/specialGroups" },
        "self": { "href": ".../server/api/authn/status" }
    }
}

后续写操作统一带两个头:Authorization: Bearer <token> 与 X-XSRF-TOKEN: <cookie 值>。

四、CRUD 实测:社区与收藏全生命周期

4.1 创建社区(201)

$ curl -s -b dscj.txt -X POST http://127.0.0.1:8139/server/api/core/communities \
    -H "Authorization: Bearer <token>" -H "X-XSRF-TOKEN: <xsrf>" \
    -H 'Content-Type: application/json' \
    -d '{"metadata":{"dc.title":[{"value":"HAL Demo Community","language":"en"}],"dc.description.abstract":[{"value":"quzuo S9 D5 evidence"}]}}'
HTTP/1.1 201

响应体即完整资源表示(实测摘录):

{ "uuid": "0f6658ff-7682-4b4e-8876-ac6d94eca2ff", "name": "HAL Demo Community",
  "handle": "123456789/7", "uniqueType": "core.community", ... }

两个实测细节:① 元数据以 DC 限定集键 + 值数组表达(dc.title 是数组,支持多值/语言标注);② 201 不带 Location 头,新资源 ID 从响应体 uuid 取。handle 前缀 123456789 来自配置项 handle.prefix(配置篇第六节),序号全局递增;Handle 解析页形如 http://192.168.1.200:8139/handle/123456789/1(实测元数据 dc.identifier.uri 原文)。

4.2 读取(200)

$ curl -s -H "Authorization: Bearer <token>" .../core/communities/0f6658ff-7682-4b4e-8876-ac6d94eca2ff
HTTP 200
name= HAL Demo Community | abstract= quzuo S9 D5 evidence

4.3 更新:PATCH 是唯一正解(PUT 负向实测)

DSpace 元数据更新走 JSON Patch 风格 operations:

$ curl -s -b dscj.txt -X PATCH .../core/communities/0f6658ff-... \
    -H "Authorization: Bearer <token>" -H "X-XSRF-TOKEN: <xsrf>" \
    -H 'Content-Type: application/json' \
    -d '[{"op":"replace","path":"/metadata/dc.title/0","value":{"value":"HAL Demo Community R2","language":"en"}}]'
HTTP/1.1 200
name now= HAL Demo Community R2

改 PUT 整资源替换?实测 405 明确告知不支持:

$ curl -s -X PUT .../core/communities ...
{"type":"about:blank","title":"Method Not Allowed","status":405,"detail":"Method 'PUT' is not supported.","instance":"/server/api/core/communities"}

4.4 收藏挂到社区下(201,parent 用查询参数)

$ curl -s -b dscj.txt -X POST ".../core/collections?parent=0f6658ff-7682-4b4e-8876-ac6d94eca2ff" \
    -H "Authorization: Bearer <token>" -H "X-XSRF-TOKEN: <xsrf>" \
    -H 'Content-Type: application/json' \
    -d '{"metadata":{"dc.title":[{"value":"HAL Demo Collection","language":"en"}]}}'
HTTP/1.1 201
uuid= b46cc234-b2d2-42f9-969d-24f09f7f81c5 | name= HAL Demo Collection | handle= 123456789/8

层级关系即时可查(HAL 嵌入链接,实测):

$ curl -s .../core/communities/0f6658ff-.../collections
total= 1 names= ['HAL Demo Collection']

4.5 删除与删除后验证(204 → 404)

$ curl -s -b dscj.txt -X DELETE .../core/collections/b46cc234-... -H ...
collection DELETE: HTTP 204
$ curl -s -b dscj.txt -X DELETE .../core/communities/0f6658ff-... -H ...
community DELETE: HTTP 204
$ curl -s .../core/communities/0f6658ff-...
HTTP 404

删除顺序需先子后父(收藏→社区);204 无响应体,删除后原 URL 读回 404。

五、检索实测:三条路径

5.1 资源集自带 search 端点

$ curl -s http://127.0.0.1:8139/server/api/core/communities/search
['findAddAuthorized', 'findAdminAuthorized', 'findEditAuthorized', 'top']

每个资源集的可检索入口以 HAL 文档形式自描述(如 top 返回最活跃社区),无需外部文档。

5.2 DSO 全局寻址:dso/find?uuid= 用 302 跳转到规范 URL

$ curl -s -i "http://127.0.0.1:8139/server/api/dso/find?uuid=01f44fb5-b1d5-4fff-91d1-17bd6546747f"
HTTP/1.1 302
Location: http://127.0.0.1:8139/server/api/core/communities/01f44fb5-b1d5-4fff-91d1-17bd6546747f
$ curl -sL "http://127.0.0.1:8139/server/api/dso/find?uuid=01f44fb5-..."
type= core.community name= Demo Community

给一个对象 UUID、不知道类型时,这个端点统一解析(curl 需 -L 跟随)。

5.3 全文检索:discover/search/objects(Solr search core)

$ curl -s "http://127.0.0.1:8139/server/api/discover/search/objects?query=HAL&size=3"
objects total= 2
self= http://127.0.0.1:8139/server/api/discover/search/objects?query=HAL&size=3

实测 total=2 正是第四节刚建又刚删的社区+收藏(查询发生在删除前)——社区/收藏元数据即时进 Solr search core,写删链路同步索引。标准分页语义在响应 page 对象(size/totalElements/totalPages/number),分面参数与排序键可叠加(fetchqueries、spellcheck=true 等)。

六、账号端点与隐私边界

  • 常理的"当前用户"端点实测 404:
$ curl -s -H "Authorization: Bearer <token>" http://127.0.0.1:8139/server/api/eperson/epersons/me
{"timestamp":"2026-11-01T13:38:29.872+00:00","status":404,"error":"Not Found","message":"An exception has occurred","path":"/server/api/eperson/epersons/me"}

正确姿势是取 authn/status 的 _links.eperson.href(第三节 3.3 实测已给出)再 GET:

$ curl -s -H "Authorization: Bearer <token>" .../eperson/epersons/bc023daf-59a5-4b22-974b-d6beb8af3f0d
name: admin user
email exposed: False
metadata keys: ['eperson.firstname', 'eperson.language', 'eperson.lastname']
  • 隐私边界实测:即使是管理员自己读自己,eperson.email 也不在返回元数据中(DSpace 默认隐藏邮箱元数据,受 properties.exposed 类配置控制)——批量导出用户邮箱需走数据库或调整暴露配置。
  • 组、权限策略、工作流等管理域端点均可从根资源 _links(groups、resourcepolicies、workflowdefinitions 等)按本节同样方法发现与调用。

七、方法与端点速查(全部实测状态码)

操作方法 + 路径实测状态码备注
API 发现GET /server/api20085 个顶层链接,契约即 HAL
OpenAPI 契约GET /server/v3/api-docs40410.1 不提供
匿名状态GET /api/authn/status200authenticated=false,兼取 XSRF Cookie
登录(无 XSRF 头)POST /api/authn/login403Invalid CSRF token
登录(Cookie+头成对)POST /api/authn/login200JWT 在 Authorization 响应头,体空
持令牌状态GET /api/authn/status200authenticated=true
过期令牌请求GET /api/eperson/epersons/<uuid>40130 分钟 TTL
建社区POST /api/core/communities201体含 uuid/handle,无 Location 头
读社区GET /api/core/communities/<uuid>200删除后同请求 404
改元数据PATCH /api/core/communities/<uuid>200operations 数组
整资源替换PUT /api/core/communities405Method 'PUT' is not supported
建收藏(带 parent)POST /api/core/collections?parent=<uuid>201handle 序号递增
层级嵌入GET /api/core/communities/<uuid>/collections200page.totalElements
删除DELETE .../<uuid>204先子后父
当前用户 meGET /api/eperson/epersons/me404改用 authn/status 的 eperson 链接
DSO 寻址GET /api/dso/find?uuid=302curl 加 -L
全文检索GET /api/discover/search/objects?query=200Solr search core
配置读取GET /api/config/properties/<key>404用 CLI dsprop 替代(配置篇)

八、排障实录

  1. 403 Invalid CSRF token:写操作 403 且凭据无误时,检查 XSRF 双要素是否成对(Cookie DSPACE-XSRF-COOKIE + 头 X-XSRF-TOKEN 同值);Cookie 作用域是 /server 路径(实测 jar 第 4 列),跨路径不带不回。
  2. 405 Method 'PUT' is not supported:别找"整资源替换",DSpace 更新语义就是 PATCH operations;单值替换路径 /metadata/dc.title/0 的尾号是数组下标。
  3. 401 但刚登录过:JWT 30 分钟过期(第三节实测 exp 差值 1800 秒),集成侧要做令牌刷新,勿长期缓存。
  4. epersons/me 404:非笔误,10.1 路由不提供该别名;从 authn/status._links.eperson.href 拿规范地址。
  5. 201 后拿不到 Location:本 API 的 201 不保证 Location 头(实测缺失),以响应体 uuid 为准。
  6. 检索"没结果":新建对象索引有秒级延迟,且 discover 默认面向条目(Item);社区/收藏可先用 dso/find 或资源集 search 端点验证存在性。

九、参考链接

  1. 站内:《DSpace 介绍与选型指南》
  2. 站内:《DSpace 10.1 Docker Compose 部署指南》
  3. 站内:《DSpace 配置参数详解(env/local.cfg/dspace.cfg 三层优先级链与关键项)》
  4. DSpace 官方文档 DSDOC10x(REST Contract 章节,查询日期 2026-11-01):https://wiki.lyrasis.org/display/DSDOC10x
  5. HAL 超媒体格式规范(查询日期 2026-11-01):https://stateless.halformat.org/spec
  6. RFC 6570 URI Template(根资源链接模板语法,查询日期 2026-11-01):https://datatracker.ietf.org/doc/html/rfc6570
DSpace REST API 使用详解(HAL 超媒体认证、CRUD 与检索实测) - Quzuo