RealWorld 后端 API 端点规范全解:Conduit 的 18 个 REST 接口、认证模型与官方测试验证
RealWorld("The mother of all demo apps")为所有后端实现定义了一套统一的 API 契约,本文以仓库中的端点规范文档为核心,完整梳理认证头约定、用户/文章/评论/收藏/标签五大类共 18 个端点的请求与响应约定、查询参数与字段要求,并结合仓库内的 OpenAPI 规范、Hurl/Bruno 测试套件说明如何对本地实现做端到端自动化验证。读完后你可以直接按此契约实现一个 RealWorld 兼容的 Conduit 风格后端,并用官方测试脚本检验其正确性。
1. 认证头约定:Authorization: Token ...
RealWorld 的受保护接口统一通过请求头携带 JWT 令牌,规范文档给出的标准形式为:
Authorization: Token jwt.token.here
- 令牌来自注册(
POST /api/users)或登录(POST /api/users/login)成功后的响应中的user.token字段; - 该约定在 OpenAPI 规范中对应
apiKey类型的 securityScheme,其描述明确要求“访问受保护资源时,必须已通过注册或登录获得有效 JWT,并通过Authorization头传入”(见 openapi.yml 第 912 行起的securitySchemes定义); - 官方 Hurl 测试中每个需要认证的请求都写成
Authorization: Token {{token}}(见 specs/api/hurl/auth.hurl 第 42 行起),这正是实现时需要对齐的格式。
2. 用户与认证端点(4 个)
2.1 登录:POST /api/users/login
无需认证,请求体示例(来自 endpoints.md):
{
"user":{
"email": "jake@jake.jake",
"password": "jakejake"
}
}
必填字段:email、password。成功返回一个 User 对象(结构见第 7 节)。OpenAPI 规范中该操作为 Login,成功状态码为 200,错误分支包括 401(凭据无效)与 422(字段校验失败,见 openapi.yml 第 22 行起)。
2.2 注册:POST /api/users
无需认证,请求体示例:
{
"user":{
"username": "Jacob",
"email": "jake@jake.jake",
"password": "jakejake"
}
}
必填字段:email、username、password,成功返回 User。从 openapi.yml 第 39 行起的 CreateUser 定义可以看出,注册成功的状态码是 201(而非 200),用户名或邮箱冲突时返回 409 Conflict——实现时注意区分这两个状态码,Hurl 测试中的断言也是 HTTP 201(见 specs/api/hurl/auth.hurl 第 1 行起)。
2.3 获取当前用户:GET /api/user
需要认证,返回当前登录用户对应的 User 对象。对应 OpenAPI 中的 GetCurrentUser。
2.4 更新用户:PUT /api/user
需要认证,请求体示例:
{
"user":{
"email": "jake@jake.jake",
"bio": "I like to skateboard",
"image": "https://i.stack.imgur.com/xHWG8.jpg"
}
}
接受的字段:email、username、password、image、bio。
从源码结构看,测试套件对更新接口的约束远比“字段可传”严格,specs/api/hurl/errors_auth.hurl 覆盖了以下边界行为:
email/username/password传空字符串应被拒绝(422);password必须至少 8 个字符(18 字符以下简单短密码会被拒,64 字符长密码应接受);- 可空字段
bio、image传空字符串时应规范化为null并持久化;显式传null对可空字段是合法操作。
这些行为对实现中的“空串归一化”逻辑(如 ORM 序列化前把 "" 转成 None)提出了明确要求。
3. 个人主页与关注端点(3 个)
3.1 获取个人主页:GET /api/profiles/:username
认证可选(游客可访问,登录后 following 字段才有意义),返回 Profile 对象。用户不存在时返回 404。
3.2 关注用户:POST /api/profiles/:username/follow
3.3 取消关注:DELETE /api/profiles/:username/follow
两者均需要认证、无额外请求参数,成功时返回操作后的 Profile 对象;目标用户不存在时返回 404(对应 OpenAPI 中 FollowUserByUsername / UnfollowUserByUsername,见 openapi.yml 第 112 行起)。Hurl 测试 specs/api/hurl/profiles.hurl 验证了 follow 后 following: true、unfollow 后回落为 false 的持久化语义。
4. 文章端点(7 个)
文章是 RealWorld 数据模型的核心,端点最多。
4.1 文章列表:GET /api/articles
默认全局返回最新文章;支持以下查询参数(均见 endpoints.md):
| 参数 | 作用 | 示例 | 默认值 |
|---|---|---|---|
tag |
按标签过滤 | ?tag=AngularJS |
不过滤 |
author |
按作者用户名过滤 | ?author=jake |
不过滤 |
favorited |
过滤某用户收藏的文章 | ?favorited=jake |
不过滤 |
limit |
限制返回条数 | ?limit=20 |
20 |
offset |
跳过条数(分页游标) | ?offset=0 |
0 |
认证可选,按最新创建时间降序返回 多篇文章(articles 数组 + articlesCount)。limit/offset 两个参数在 OpenAPI 中定义为共享参数(openapi.yml 第 895 行起的 offsetParam、limitParam),分页行为由 specs/api/hurl/pagination.hurl 验证:limit=1 时先返回最新一篇,offset=1 后翻页拿到次新一篇。
4.2 关注流:GET /api/articles/feed
支持同样的 limit 与 offset 参数,必须认证,返回当前用户所关注用户发布的文章,按最新优先排序。specs/api/hurl/feed.hurl 验证了“新用户 feed 为空 → 关注后 feed 出现其文章”的完整链路。
4.3 获取单篇文章:GET /api/articles/:slug
无需认证,按 slug 返回 单篇文章;slug 不存在返回 404。
4.4 创建文章:POST /api/articles
需要认证,请求体示例:
{
"article": {
"title": "How to train your dragon",
"description": "Ever wonder how?",
"body": "You have to believe",
"tagList": ["reactjs", "angularjs", "dragons"]
}
}
必填字段:title、description、body;可选字段 tagList(字符串数组)。标题/描述/正文任一为空字符串时返回 422(对应 specs/api/hurl/errors_articles.hurl 中的 09–11 组断言)。
4.5 更新文章:PUT /api/articles/:slug
需要认证,可传可选字段 title、description、body;当 title 变更时 slug 也必须随之重新生成。请求体示例:
{
"article": {
"title": "Did you train your dragon?"
}
}
规范对 slug 的约定值得特别注意(原文档原文):
slug 是文章的 URL 标识符。规范只要求它必须是唯一字符串,用于 fetch/update/delete 文章——重复标题也必须产生不同的 slug。如何派生由实现自定(常见做法是 title 的 kebab-case),测试套件不强制任何特定格式。
从源码结构看,specs/api/hurl/errors_articles.hurl 中专门有两组用例:12-duplicate-titles-are-allowed-each-gets-a-unique-slug(两个相同标题的文章各自获得不同 slug)与 13-update-article-without-taglist-tags-should-be-preserved(更新时不传 tagList 应保留原有标签;传空数组则清空标签;tagList 为 null 会被拒绝)——这些都是纯靠读接口列表容易漏掉的持久化细节。
4.6 删除文章:DELETE /api/articles/:slug
需要认证,且只能删除自己的文章:他人删除应返回 403(specs/api/hurl/errors_authorization.hurl 中 04-user-b-tries-to-delete-403 用例验证)。slug 不存在返回 404。
5. 评论端点(3 个)
5.1 发表评论:POST /api/articles/:slug/comments
需要认证,请求体示例:
{
"comment": {
"body": "His name was my name too."
}
}
必填字段:body;成功返回创建后的 Comment 对象。文章不存在或 body 为空分别返回 404/422。
5.2 获取文章评论:GET /api/articles/:slug/comments
认证可选,返回 多条评论。
5.3 删除评论:DELETE /api/articles/:slug/comments/:id
需要认证。权限语义为“谁评论的谁删”:非作者删除他人评论返回 403 且评论必须仍然存活(specs/api/hurl/errors_authorization.hurl 中 07/08 用例显式验证了这一点);specs/api/hurl/comments.hurl 中还包含“选择性删除”场景——删掉一条后另一条仍然存在。
6. 收藏与标签端点(3 个)
6.1 收藏文章:POST /api/articles/:slug/favorite
6.2 取消收藏:DELETE /api/articles/:slug/favorite
均需要认证、无额外参数,成功返回 Article 对象——注意返回的是操作后的完整文章对象,favorited 与 favoritesCount 应已更新。specs/api/hurl/favorites.hurl 验证了收藏状态持久化,以及第 2 节提到的 ?favorited=jake 过滤能力。
6.3 获取标签列表:GET /api/tags
无需认证,返回字符串数组:
{
"tags": [
"reactjs",
"angularjs"
]
}
7. 响应对象结构速查
端点规范中的“返回 User / Profile / Article / Comment”均指向统一的响应格式文档(api-response-format.md),关键点:
- User:
email、token、username、bio、image(bio/image可为null); - Profile:
username、bio、image、following; - Single Article:
slug、title、description、body、tagList、createdAt、updatedAt、favorited、favoritesCount,并内嵌authorProfile; - Multiple Articles:
articles数组 +articlesCount; - Comment:
id、createdAt、updatedAt、body+ 内嵌authorProfile。
响应头需保证 Content-Type: application/json; charset=utf-8。
一个重要的版本行为变更(原文档标注):自 2024-08-16 起,GET /api/articles 与 GET /api/articles/feed 不再返回文章的 body 字段(性能考虑)。因此列表接口返回的文章对象没有 body,只有单篇接口才有完整正文——实现时不要在这两个列表端点输出 body。
8. 错误码约定
端点失败时的状态码语义在 error-handling.md 中统一定义:
- 422:字段校验失败,错误体形如
{"errors": {"body": ["can't be empty"]}}(字段名为键、错误消息数组为值); - 401:需要认证但未提供;
- 403:请求合法但当前用户无权限;
- 404:资源不存在。
这套语义在各分类的 Hurl 错误测试目录中逐条落实,可作为实现后的对照清单:specs/api/hurl/ 下的 errors_auth.hurl、errors_articles.hurl、errors_authorization.hurl、errors_comments.hurl、errors_profiles.hurl。
9. 用官方测试套件验证你的实现
规范文档的 Introduction(introduction.md)明确:OpenAPI 规范与 Hurl 测试才是契约的最终权威,散文式文档与测试不一致时,以测试为准。
仓库提供了两套可直接对本地后端运行的测试:
Hurl 方式(Hurl 文件是唯一事实来源,见 specs/api/README.md):
HOST=http://localhost:3000/api ./run-api-tests-hurl.sh
run-api-tests-hurl.sh 内部以 hurl --test --jobs 1 串行执行 hurl/ 目录下全部用例,并注入两个变量:
host:API 基地址(脚本默认值为http://localhost:8000,通常按 README 用HOST环境变量覆盖);uid:默认取时间戳+进程号,用于生成互不冲突的用户名/邮箱,保证同一后端可重复运行测试。
Bruno 方式(交互式集合,由 Hurl 自动生成并保持同步):
HOST=http://localhost:3000/api ./run-api-tests-bruno.sh
也可以直接用 Bruno 打开 specs/api/bruno/ 目录逐请求调试。按 specs/api/README.md 说明,Bruno 集合通过 make bruno-generate 从 Hurl 生成,并经 CI 以 make bruno-check 保持同步,因此两套集合的覆盖面一致。
10. 小结
RealWorld 的后端契约由三部分构成:端点清单(18 个接口,覆盖认证、用户、个人主页、关注、文章、评论、收藏、标签)、统一响应对象格式(User/Profile/Article/Comment/Tags)与四类状态码语义(422/401/403/404)。实现时的重点难点集中在:注册返回 201、更新用户时 bio/image 空串归一化为 null、文章标题变更后重新派生 slug 且重复标题必须 slug 唯一、列表接口自 2024-08-16 起不再输出 body、以及各类越权操作的 403/404 精确区分。以 specs/api/openapi.yml 为机器可读契约、以 specs/api/hurl/ 测试套件为验收基准,即可交付一个完全兼容 RealWorld 规范的 Conduit 后端。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00