进阶 docs.flarum.org 2026-10-15 14:50:53 · 19 阅读
第46章 调用 REST API
版本:2.x 调用 REST API
Flarum 对外提供 REST API,供单页应用(SPA)使用,同时也开放给外部脚本调用。 该 API 遵循 JSON:API 规范中定义的最佳实践。info 如需为 REST API 扩展新的端点,请参阅开发文档中的「API 与数据流」章节。
身份认证
单页应用通过会话 Cookie 进行 API 身份验证。 外部脚本可使用无状态认证方式,即 API Key 或 Access Token。 GET 请求端点可在无认证情况下使用,但仅返回对游客可见的内容。 由于 CSRF 保护机制,其他端点通常必须经过认证才能使用。API Keys
API Key 是脚本、工具和集成系统与 Flarum 交互的主要方式。创建
目前尚无管理 API Key 的界面,但可以在数据库的api_keys 表中手动创建。
可填写的字段如下:
key:生成一个长且唯一的令牌(建议使用 40 位字母数字字符)并填入此处,该值将用于 Authorization 请求头。user_id:可选。若设置此值,该 Key 仅能以指定用户的身份操作。
id:由 MySQL 自增字段自动填充。allowed_ips:尚未实现。scopes:尚未实现。created_at:可设置为任意日期,但本意用于记录 Key 的创建时间。last_activity_at:令牌被使用时会自动更新。
使用
在每个 API 请求的 Authorization 请求头中附加你的 Key 值。 然后在请求头末尾指定要交互的用户 ID:Authorization: Token YOUR_API_KEY_VALUE; userId=1
如果在数据库中为该 Key 设置了 user_id 值,则 userId= 参数会被忽略。
否则,可以设置为数据库中存在的任意有效用户 ID。
Access Tokens
Access Token 是绑定到特定用户的短期令牌。 这些令牌在幕后用于 Cookie 会话管理。 在无状态 API 请求中使用 Access Token,效果等同于常规会话。 每次使用该令牌时,用户的最后活动时间都会得到更新。创建
所有用户均允许创建 Access Token。 使用/api/token 端点并携带用户凭据来创建令牌:
POST /api/token HTTP/1.1
{
"identification": "Toby",
"password": "pass7word"
}
HTTP/1.1 200 OK
{
"token": "YACub2KLfe8mfmHPcUKtt6t2SMJOGPXnZbqhc3nX","userId": "1"
}
目前共有 3 种 token 类型,但其中只有 2 种可通过 REST API 创建。 session token 在 1 小时无活动后过期。这是默认的 token 类型。 session_remember token 在 5 年无活动后过期。通过在请求属性中指定 remember=1 即可获取。 developer token 永不过期。目前只能在数据库中进行手动创建。 注销操作仅删除该会话使用的 access token。全局注销(POST /logout 附带 ?global=1)以及更改用户密码,会删除该用户所有的 access token —— 包括 developer token。 使用方式 通过 Authorization 请求头将返回的 token 值附加到每个 API 请求中: Authorization: Token YACub2KLfe8mfmHPcUKtt6t2SMJOGPXnZbqhc3nX CSRF 防护 大多数 POST/PUT/DELETE API 端点都具备跨站请求伪造(Cross-site request forgery)防护。 这意味着,在没有身份验证的情况下,无法执行无状态请求。 使用 API Key 或 Access Token 时,会绕过 CSRF 防护。 端点 这部分文档仍在编写中。 我们正在研究提供端点自动化文档的方案。 每个扩展都会新增端点和属性,因此难以提供完整的端点文档。 发现端点的好方法是使用浏览器开发者工具,查看单页应用发出的请求。 下面列出了一些常用端点的示例。 JSON 已截断以便阅读。 获取讨论列表 GET /api/discussions {
"links": {
"first": "https://flarum.tld/api/discussions",
"next": "https://flarum.tld/api/discussions?page%5Boffset%5D=20"
},
"data": [
{
"type": "discussions",
"id": "234",
"attributes": {
"title": "Lorem Ipsum",
"slug": "234-lorem-ipsum",
"commentCount": 10,
"participantCount": 3,
"createdAt": "2022-01-01T10:20:30+00:00",
"lastPostedAt": "2022-01-05T10:20:30+00:00",
"lastPostNumber": 10,
"canReply": true,
"canRename": true,
"canDelete": true,
"canHide": true,
"isHidden": true,
"hiddenAt": "2022-01-06T10:20:30+00:00",
"lastReadAt": "2022-01-02T10:20:30+00:00",
"lastReadPostNumber": 2,
"isApproved": true,
"isLocked": false,
"canLock": true,
"isSticky": false,
"canSticky": true,
"canMerge": true,
"subscription": null
},
"relationships": {
"user": {
"data": {
"type": "users",
"id": "1"
}
},
"lastPostedUser": {
"data": {
"type": "users",
"id": "64"
}
},
"tags": {
"data": [
{
"type": "tags",
"id": "3"
}
]
},
"firstPost": {
"data": {
"type": "posts",
"id": "668"
}
}
}
},
{
"type": "discussions",
"id": "234",
"attributes": {
// [...]
},
"relationships": {
// [...]
}
},
// [...] 更多讨论
],
"included": [
{
"type": "users",
"id": "1",
"attributes": {
"username": "Admin",
"displayName": "Admin",
"avatarUrl": null,
"slug": "1"
}
},
{
"type": "users",
"id": "64",
"attributes": {
"username": "Flarum",
"displayName": "Flarum",
"avatarUrl": "https://flarum.tld/assets/avatars/Z4hEncw0ndVqZ8be.png",
"slug": "64"
}
},
{
"type": "tags",
"id": "3",
"attributes": {
"name": "Welcome",
"description": "Post interesting things here",
"slug": "welcome",
"color": "#888",
"backgroundUrl": null,
"backgroundMode": null,
"icon": "fas fa-bullhorn",
"discussionCount": 30,
"position": 1,
"defaultSort": null,
"isChild": false,
"isHidden": false,
"lastPostedAt": "2022-01-05T10:20:30+00:00",
"canStartDiscussion": true,
"canAddToDiscussion": true,
"isRestricted": false
}
},
{
"type": "posts",
"id": "668",
"attributes": {
"number": 1,
"createdAt": "2022-01-01T10:20:30+00:00",
"contentType": "comment",
"contentHtml": "
Hello World
"}
},
// [...] 其他 discussion 的 includes
]
}
创建 discussion POST /api/discussions {
"data":{
"type": "discussions",
"attributes": {
"title": "Lorem Ipsum",
"content": "Hello World"
},
"relationships": {
"tags": {
"data": [
{
"type": "tags",
"id": "1"
}
]
}
}
}
}
响应中包含新讨论的 ID: {
"data": {
"type": "discussions",
"id": "42",
"attributes": {
"title": "Lorem Ipsum",
"slug": "42-lorem-ipsum",
"commentCount": 1
// [...] 其他属性
},
"relationships": {
"posts": {
"data": [
{
"type": "posts",
"id": "58"
}
]
},
"user": {
"data": {
"type": "users",
"id": "1"
}
},
// [...] 其他 relationships
}
},
"included":[
{
"type": "posts",
"id": "38",
"attributes": {
"number": 1,
"contentType": "comment",
"contentHtml": "\u003Cp\u003EHello World\u003C\/p\u003E"
// [...] 其他属性
}
}
// [...] 其他 includes
]
}
创建用户 POST /api/users {
"data": {
"attributes": {
"username": "Flarum",
"email": "[email protected]",
"password": "correcthorsebatterystaple"
}
}
}
错误 Flarum 使用多种 HTTP 状态码,并包含符合 JSON:API 错误规范的错误描述。 以下是使用 REST API 时可能遇到的一些常见错误: CSRF Token 不匹配 如果收到带有 csrf_token_mismatch 消息的 400 HTTP 错误,说明 Authorization 头缺失或无效,Flarum 尝试通过 session cookie 进行身份验证。 {
"errors": [
{
"status": "400",
"code": "csrf_token_mismatch"
}
]
}
验证错误 验证错误以 422 HTTP 状态码返回。 无效字段的名称作为 pointer 值返回。 单个字段可能同时存在多个错误。 {
"errors": [
{
"status": "422",
"code": "validation_error",
"detail": "该用户名已被占用。",
"source": {
"pointer": "\/data\/attributes\/username"
}
},
{
"status": "422",
"code": "validation_error",
"detail": "该邮箱已被占用。",
"source": {
"pointer": "\/data\/attributes\/email"
}
}
]
}