realworld 后端 API 响应格式规范:六大资源 JSON 结构详解与 Hurl 测试验证实践
realworld 后端 API 响应格式规范六大资源 JSON 结构详解与 Hurl 测试验证实践【免费下载链接】realworldThe mother of all demo apps — Exemplary fullstack Medium.com clone powered by React, Angular, Node, Django, and many more项目地址: https://gitcode.com/GitHub_Trending/re/realworld本文围绕 realworld 官方文档docs/src/content/docs/specifications/backend/api-response-format.md展开完整解读该 Medium 风格全栈应用对后端实现提出的响应格式契约User、Profile、Article、Comment、Tags 六类 JSON 对象的结构、可空字段语义与时间戳格式并结合 OpenAPI 规范 中的 schema 定义和 Hurl 测试套件 的真实断言说明每一条格式要求是如何被自动化测试逐字段校验的。读完本文你可以对照规范实现自己的后端并用仓库自带的 Hurl 用例逐条验证响应结构是否合规。契约地位文档只是摘要测试才是真相在 后端规范入口文档 中明确声明各文档页面只是对契约的文字摘要真正定义契约的是 OpenAPI 规范 和 Hurl 测试套件——当文字描述与测试不一致时以测试为准。因此本文在逐条解读响应结构的同时均附上 Hurl 断言作为可执行依据。所有后端实现首先必须满足的通用要求Content-Type 必须正确响应需返回Content-Type: application/json; charsetutf-8。这是 api-response-format.md 对 JSON Objects returned by API 的第一条硬性要求。顶层包装键envelope key固定每个成功响应都用资源名单数或复数作为唯一顶层键如user、profile、article、articles、comment、comments、tags。这一命名与 OpenAPI 中各 Response schema 的required字段一一对应例如UserResponse要求顶层必含userMultipleArticlesResponse要求必含articles和articlesCount见 openapi.yml 中components.responses的定义。字段命名采用 camelCase如tagList、createdAt、favoritesCount不要用 snake_case。时间戳采用 ISO 8601date-timeOpenAPI 中createdAt/updatedAt均声明为format: date-time例如2016-02-18T03:22:56.637Z。Hurl 测试通过正则^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}来校验该格式。可空字段返回 JSONnull而非缺省或空字符串bio、image等字段在 User 和 Profile 对象中允许为nullOpenAPI 中声明为type: [string, null]。User认证相关响应对象用于注册POST /api/users、登录POST /api/users/login、获取当前用户GET /api/user和更新用户PUT /api/user四类端点标准响应如下{ user: { email: jakejake.jake, token: jwt.token.here, username: jake, bio: null, image: null } }结合 OpenAPI 的 User schema 可以确认字段要求字段类型必填说明emailstring是用户邮箱tokenstring是JWT 令牌用于后续受保护请求的Authorization: Token xxx头usernamestring是用户名biostring | null是个人简介可为nullimagestring | null是头像 URL可为null三个字段语义要点均来自 Hurl 测试 auth.hurl 的实际断言bio与image未设置时必须是null注册和登录响应的断言为$.user.bio null、$.user.image null而不是省略该键。空字符串会被规范化为nullHurl 中专门有 Update user bio to empty string - should normalize to null 用例PUT /api/user提交bio: 后断言$.user.bio null且后续GET /api/user验证该规范化结果被持久化。image同理。token必须是非空字符串断言为$.user.token isString且$.user.token not isEmpty。测试流程正是靠这一字段做后续请求的鉴权[Captures] reg_token: jsonpath $.user.token。Profile用户公开资料对象Profile 是 User 的对外视图——不包含email和token额外增加following布尔量{ profile: { username: jake, bio: I work at statefarm, image: https://api.realworld.io/images/smiley-cyrus.jpg, following: false } }对应端点获取资料GET /api/profiles/{username}鉴权可选、关注POST /api/profiles/{username}/follow、取关DELETE /api/profiles/{username}/follow鉴权必须。OpenAPI 中Profileschema 的必填字段为bio、following、image、username其中bio和image允许nullfollowing为 boolean。Profile 还以嵌套对象形式出现在 Article 和 Comment 中见下文的author字段其following值取决于当前请求是否携带用户身份未鉴权时通常为false。这意味着你的后端在渲染文章、评论列表时需要按当前观察者计算following而不是直接复存数据库值。Single Article单篇文章响应对象文章创建POST /api/articles201、读取GET /api/articles/{slug}200、更新PUT /api/articles/{slug}200、收藏/取消收藏POST|DELETE /api/articles/{slug}/favorite200均返回该结构{ article: { slug: how-to-train-your-dragon, title: How to train your dragon, description: Ever wonder how?, body: It takes a Jacobian, tagList: [dragons, training], createdAt: 2016-02-18T03:22:56.637Z, updatedAt: 2016-02-18T03:48:35.824Z, favorited: false, favoritesCount: 0, author: { username: jake, bio: I work at statefarm, image: https://i.stack.imgur.com/xHWG8.jpg, following: false } } }OpenAPIArticleschema 的必填字段为author、body、createdAt、description、favorited、favoritesCount、slug、tagList、title、updatedAt。几个容易被实现者忽略的行为articles.hurl 都有对应断言tagList顺序保留且是字符串数组创建时提交的[d_xxx, t_xxx]在响应中被断言为tagList[0] d_xxx、tagList[1] t_xxx。新建文章初始状态favorited false、favoritesCount 0、author.username等于创建者。更新时间戳语义更新正文后断言$.article.createdAt保持不变、$.article.updatedAt发生变化测试先[Captures]捕获创建时的两个时间戳再在PUT后比对。更新时省略tagList则保留原标签仅提交body的PUT请求后断言tagList仍为 2 项且内容不变而显式提交tagList: []表示清空tagList: null则应被拒绝Hurl 断言该请求返回 422。Multiple Articles文章列表响应对象与 body 移除规则列表端点GET /api/articles支持tag/author/favorited过滤参数及offset/limit分页与GET /api/articles/feed返回如下结构{ articles: [{ slug: how-to-train-your-dragon, title: How to train your dragon, description: Ever wonder how?, tagList: [dragons, training], createdAt: 2016-02-18T03:22:56.637Z, updatedAt: 2016-02-18T03:48:35.824Z, favorited: false, favoritesCount: 0, author: { username: jake, bio: I work at statefarm, image: https://i.stack.imgur.com/xHWG8.jpg, following: false } }, { slug: how-to-train-your-dragon-2, title: How to train your dragon 2, description: So toothless, tagList: [dragons, training], createdAt: 2016-02-18T03:22:56.637Z, updatedAt: 2016-02-18T03:48:35.824Z, favorited: false, favoritesCount: 0, author: { username: jake, bio: I work at statefarm, image: https://i.stack.imgur.com/xHWG8.jpg, following: false } }], articlesCount: 2 }关键变更原文档的 caution 提示自 2024-08-16 起出于性能考虑获取文章列表的端点不再返回文章的body字段。受影响的端点为GET /api/articlesGET /api/articles/feed这一契约变更在仓库中有两处可交叉验证的证据OpenAPI 规范 的MultipleArticlesResponse中articles数组内联定义的必填字段为author、createdAt、description、favorited、favoritesCount、slug、tagList、title、updatedAt——没有body而SingleArticleResponse引用的Articleschema 是包含body的。articles.hurl 对全部五种列表场景无鉴权全局列表、按作者过滤、带鉴权列表、带鉴权按作者过滤、按 tag 过滤都显式断言jsonpath $.articles[0].body not exists同时逐字段校验title/slug/description为字符串、tagList为列表、时间戳匹配 ISO 8601 正则、favorited为布尔、favoritesCount为整数。分页参数在 OpenAPI 的 parameters 定义 中有约束offset为不小于 0 的整数跳过前 N 条limit为不小于 1 的整数、默认 20。articlesCount表示满足过滤条件的总条数与当前页的articles长度无关。仓库中 pagination.hurl 用limit1配合offset逐页取回文章验证了最近优先 分页的排序语义。Comments单条与多条评论响应对象单条评论创建POST /api/articles/{slug}/comments返回 201{ comment: { id: 1, createdAt: 2016-02-18T03:22:56.637Z, updatedAt: 2016-02-18T03:22:56.637Z, body: It takes a Jacobian, author: { username: jake, bio: I work at statefarm, image: https://i.stack.imgur.com/xHWG8.jpg, following: false } } }多条评论GET /api/articles/{slug}/comments鉴权可选{ comments: [{ id: 1, createdAt: 2016-02-18T03:22:56.637Z, updatedAt: 2016-02-18T03:22:56.637Z, body: It takes a Jacobian, author: { username: jake, bio: I work at statefarm, image: https://i.stack.imgur.com/xHWG8.jpg, following: false } }] }OpenAPICommentschema 必填字段为author、body、createdAt、id、updatedAt其中id为整数Hurl 断言$.comment.id isInteger。comments.hurl 的验证要点创建评论后按id捕获变量用于后续DELETE /api/articles/{slug}/comments/{id}204 无响应体。列表断言$.comments为列表且元素级校验id/body/时间戳格式/author.username并区分了带鉴权与不带鉴权两种列表请求——两种情况下响应结构必须一致。List of Tags标签列表响应对象GET /api/tags无需鉴权返回字符串数组且 OpenAPITagsResponse要求顶层键tags必须存在{ tags: [ reactjs, angularjs ] }tags.hurl 的测试流程展示了该端点的典型用法先注册用户并创建带标签的文章tagList: [h_xxx, t_xxx]再断言$.tags为列表、长度不小于 1、包含刚创建的两个标签且各元素为字符串。错误响应统一的多态错误信封Error handling 文档 定义了与上述成功响应并列的失败响应格式——所有校验失败返回422其余状态码为401未提供鉴权、403无权操作、404资源不存在。错误体统一为{ errors:{ body: [ cant be empty ] } }即顶层errors对象的键是出错字段/资源名值为字符串数组。OpenAPI 的GenericErrorModelschema 与此一致errors为键到字符串数组的对象。实际状态码对应的键名约定可从 OpenAPI 各错误响应示例与 Hurl 断言中读出状态码场景键名示例依据401缺少有效 tokentoken: [is missing]OpenAPIUnauthorized响应示例409注册时用户名/邮箱已被占用username: [has already been taken]OpenAPIConflictError示例404资源不存在article: [not found]、resource: [not found]OpenAPINotFound示例articles.hurl 删除后断言$.errors.article[0] not found422字段校验失败title: [cant be blank]等OpenAPIGenericError示例错误键名在 404/403 场景下标识资源类型article、comment、profile 等这一点直接写在 OpenAPI 对NotFound/Forbidden响应的 description 中实现时请让键名与资源类型对应方便前端和测试稳定解析。用 Hurl 测试套件验证你的实现仓库自带的 Hurl 集合覆盖了上述全部响应格式是契约的可执行形态。运行方式见 run-api-tests-hurl.sh# 安装 hurl 后指向你的本地 API 运行全部用例 HOSThttp://localhost:8000 ./specs/api/run-api-tests-hurl.sh # 或只跑与响应格式直接相关的文件 HOSThttp://localhost:8000 ./specs/api/run-api-tests-hurl.sh specs/api/hurl/auth.hurl specs/api/hurl/articles.hurl脚本细节默认HOST为http://localhost:8000每轮运行自动生成uid变量时间戳进程号使各用例注册互不冲突的用户以--test --jobs 1串行执行保证各文件内的前置请求注册、建文章先于断言发生。你也可以用 Bruno 集合 在 GUI 中逐条查看等效请求两套工具由 hurl-to-bruno.js 转换保持同步。对照检查清单实现者视角所有成功响应是否为application/json; charsetutf-8顶层包装键user/profile/article/articles/comment/comments/tags是否拼写正确bio/image空值是否输出null且提交空字符串时是否被规范化为null并持久化时间戳是否符合YYYY-MM-DDTHH:mm:ssdate-time格式GET /api/articles与GET /api/articles/feed是否已剔除body且articlesCount始终返回列表接口是否支持offset0/limit1默认 20错误响应是否为 422/401/403/404 {errors: {key: [messages]}}信封。只要这七条全部通过 Hurl 断言你的后端就满足了 api-response-format.md 定义的完整响应契约可以与 realworld 生态中的任何前端实现互通。【免费下载链接】realworldThe mother of all demo apps — Exemplary fullstack Medium.com clone powered by React, Angular, Node, Django, and many more项目地址: https://gitcode.com/GitHub_Trending/re/realworld创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

网络安全大模型训练数据获取与清洗实战指南

网络安全大模型训练数据获取与清洗实战指南

做网络安全大模型,最磨人的往往不是模型结构,也不是算力排期,而是训练数据。模型结构可以抄开源方案,算力可以慢慢攒,但网络安全这个领域的语料,既不像通用文本那样满大街都是,也不像代码数据那…

2026/11/2 15:58:27 阅读更多 →
SPH流体仿真GPU加速实战:从CUDA代码到工程落地

SPH流体仿真GPU加速实战:从CUDA代码到工程落地

简介:本资源是一套基于CUDA加速的光滑粒子流体力学(SPH)高性能仿真代码,面向计算流体力学、天体物理模拟、爆炸冲击与自由表面流动等领域的科研人员及高年级本科生/研究生,解决传统网格方法在复杂边界和大变形问题中建…

2026/10/24 12:00:58 阅读更多 →
best.pt推理实战:YOLO统一封装图片/视频/摄像头目标检测代码

best.pt推理实战:YOLO统一封装图片/视频/摄像头目标检测代码

如果只给你一个训练好的best.pt,要求在图片、本地视频和摄像头上都能跑目标检测,你的代码会怎么写?很多人训练完只看过训练日志,真正落地时反而在“推理脚本”这里卡住:调用 API 不熟悉、视频抽帧太慢、摄像头流又不知…

2026/10/25 13:49:53 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/4 1:35:24 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/3 10:57:27 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/3 11:35:49 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/4 6:23:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/3 10:47:27 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/4 19:19:54 阅读更多 →

日新闻

周新闻

月新闻