news 2026/9/25 7:10:15

使用 API Blueprint 描述超媒体 API:Polls Hypermedia API 实战范本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 API Blueprint 描述超媒体 API:Polls Hypermedia API 实战范本
  • 文档
  • API设计
  • 教程

【免费下载链接】api-blueprint

API Blueprint

项目地址:https://gitcode.com/gh_mirrors/ap/api-blueprint
点击查看免费下载

API Blueprint 是一套建立在 Markdown 语义之上的 Web API 描述语言,而超媒体(Hypermedia)则要求 API 响应本身携带"下一步能做什么"的导航信息。本文以仓库中的 examples/Polls Hypermedia API.md 为完整范本,逐段拆解如何用 API Blueprint 描述一个同时以Siren(application/vnd.siren+json)与HAL(application/hal+json)两种媒体类型输出、具备超媒体控件的投票 API。读完本文,你将掌握元数据节、URI 模板与参数节、Relation 关系节、多响应事务示例等 API Blueprint 核心语法的实战用法,并能独立编写一份可被解析器、模拟服务器与文档工具直接消费的超媒体 API 蓝图。

一、为什么需要"超媒体版"蓝图:从 Polls API 到 Polls Hypermedia API

仓库的examples/目录下同时存在两个同源但设计取向不同的示例:

  • examples/Polls API.md —— 传统"数据驱动"版本。响应体直接给出url字段(如"url": "/questions/1"),并通过 HTTPLink头(如Link: </questions?page=2>; rel="next")传递分页关系,文档还明确建议客户端"跟随url链接值或Link/Location头,而不是自行拼接 URL"。
  • examples/Polls Hypermedia API.md —— 超媒体版本。同样的资源改用Siren与HAL两种媒体类型表达,用links、actions、entities、_links、_embedded等标准结构把"可执行的操作"直接内嵌进响应体。

两份文档都使用FORMAT: 1A与HOST: http://polls.apiblueprint.org/开头。根据 API Blueprint Specification.md 的定义,FORMAT、HOST属于元数据节(Metadata section):以键: 值形式写在文档最开头,键值对按行分隔,遇到第一个非键值对的 Markdown 元素即结束。FORMAT: 1A声明蓝图遵循 Format 1A(本仓库规范版本为 revision 9),HOST则给出模拟/测试时的基准地址。

对比两份文档可以清晰看到:超媒体并不是新增 API 资源,而是同一资源的"表达方式"升级。因此,一份优秀的超媒体蓝图往往同时写出多种媒体类型的响应示例,这正是本文范本的核心价值。

二、入口点资源:用超媒体"引导"客户端

Polls Hypermedia API 的第一个资源是入口点(Entry Point),URI 模板为/:

# Polls API Root [/] This resource does not have any attributes. Instead it offers the initial API affordances. ## Retrieve the Entry Point [GET] + Response 200 (application/vnd.siren+json) { "links": [ { "rel": [ "questions" ], "href": "/questions" } ] } + Response 200 (application/hal+json) { "_links": { "questions": { "href": "/questions" } } }

语法要点:

  1. 资源节(Resource section):由方括号内的 URI 模板 定义,形如# <标识符> [<URI 模板>]。这里的标识符是Polls API Root。
  2. 动作节(Action section):由## <标识符> [<HTTP 方法>]定义,这里是Retrieve the Entry Point [GET]。
  3. 双响应示例:同一个GET动作下挂了两个+ Response 200,分别标注媒体类型application/vnd.siren+json与application/hal+json。依据规范中 Response section 的说明,响应节定义应当包含 HTTP 状态码作为标识符,并可选携带媒体类型。

Siren 与 HAL 对"入口点"的建模高度一致:入口点资源没有业务属性,只提供questions链接指向/questions——客户端不需要硬编码任何业务 URL,跟随链接即可进入资源图。Siren 用links数组 +rel,HAL 用_links对象 + 相对链接键,二者语义等价。

三、Questions Collection:URI 模板、参数节与列表响应

## Questions Collection [/questions{?page}] + Parameters + page: 1 (optional, number) - The page of questions to return

3.1 URI 模板中的查询参数

资源 URI 模板[/questions{?page}]使用了 RFC 6570 的form-style query 操作符?。规范附录 URI Templates 指出:API Blueprint 使用 RFC 6570 子集,{?var}展开为?var=value,{&var}用于追加查询项(如?path=test{&vartwo}展开为?path=test&vartwo=hello)。

3.2 参数节语法

+ Parameters是 URI parameters section,其子项遵循固定格式:

+ <参数名>: `<示例值>` (<类型>, required | optional) - <描述>

本示例中page: 1 (optional, number)表示:参数名为page,示例值1,类型number,可选(optional);若省略required | optional修饰符,默认按required处理;省略类型时默认string。还可通过+ Default:给出默认值、+ Members给出枚举值(枚举时应把类型写为enum[<type>])。参数节应只描述父级 URI 模板中出现的参数。

3.3 List All Questions [GET]:Siren 完整响应

### List All Questions [GET] + Relation: questions + Response 200 (application/vnd.siren+json) { "actions": [ { "name": "add", "href": "/questions", "method": "POST", "type": "application/json", "fields": [ { "name": "question" }, { "name": "choices" } ] } ], "links": [ { "rel": [ "next" ], "href": "/questions?page=2" }, { "rel": [ "self" ], "href": "/questions" } ], "entities": [ { "actions": [ { "name": "delete", "href": "/questions/1", "method": "DELETE" } ], "rel": [ "question" ], "properties": { "published_at": "2014-11-11T08:40:51.620Z", "question": "Favourite programming language?" }, "links": [ { "rel": [ "self" ], "href": "/questions/1" } ], "entities": [ { "actions": [ { "name": "vote", "href": "/questions/1/choices/1", "method": "POST" } ], "rel": [ "choice" ], "properties": { "choice": "Swift", "votes": 2048 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/1" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/2", "method": "POST" } ], "rel": [ "choice" ], "properties": { "choice": "Python", "votes": 1024 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/2" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/3", "method": "POST" } ], "rel": [ "choice" ], "properties": { "choice": "Objective-C", "votes": 512 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/3" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/4", "method": "POST" } ], "rel": [ "choice" ], "properties": { "choice": "Ruby", "votes": 256 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/4" } ] } ] } ] }

这个响应是超媒体设计的典型示范,三层嵌套清晰可见:

  • 集合级actions声明"添加问题"能力(name: add,method: POST,含fields表单定义);
  • 集合级links提供next(分页)与self导航;
  • entities中的每个问题(rel: question)自带delete动作、self链接,以及内嵌的choice实体列表;每个选项(rel: choice)又自带vote动作与self链接。

3.4 同一响应的 HAL 表达

+ Response 200 (application/hal+json) { "_links": { "self": { "href": "/questions" }, "next": { "href": "/questions?page=2" } }, "_embedded": { "question": [ { "_links": { "self": { "self": "/questions/1" } }, "_embedded": { "choice": [ { "_links": { "self": { "self": "/questions/1/choices/1" } }, "choice": "Swift", "votes": 2048 }, { "_links": { "self": { "self": "/questions/1/choices/2" } }, "choice": "Python", "votes": 1024 }, { "_links": { "self": { "self": "/questions/1/choices/3" } }, "choice": "Objective-C", "votes": 512 }, { "_links": { "self": { "self": "/questions/1/choices/4" } }, "choice": "Ruby", "votes": 256 } ] }, "question": "Favourite programming language?", "published_at": "2014-11-11T08:40:51.620Z" } ] } }

HAL 用_links承载导航、_embedded承载内嵌资源,与 Siren 的links/entities一一对应。在同一个动作下并列写出两种媒体类型的响应,正是 API Blueprint 多事务示例(multiple transaction examples)能力的体现——规范在 Action section 中说明,一个动作可以包含多个请求/响应分组,每个分组代表一个完整事务示例,且同组内多个请求/响应应使用不同标识符(这里靠媒体类型区分)。

3.5 Create a New Question [POST]:请求体 + 双媒体响应

### Create a New Question [POST] You may create your own question using this action. It takes a JSON object containing a question and a collection of answers in the form of choices. + question (string) - The question + choices (array[string]) - A collection of choices. + Relation: create + Request (application/json) { "question": "Favourite programming language?", "choices": [ "Swift", "Python", "Objective-C", "Ruby" ] } + Response 201 (application/vnd.siren+json) { "actions": [ { "name": "delete", "href": "/questions/1", "method": "DELETE" } ], "properties": { "published_at": "2014-11-11T08:40:51.620Z", "question": "Favourite programming language?" }, "links": [ { "rel": [ "self" ], "href": "/questions/1" } ], "entities": [ { "actions": [ { "name": "vote", "href": "/questions/1/choices/1", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Swift", "votes": 2048 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/1" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/2", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Python", "votes": 1024 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/2" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/3", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Objective-C", "votes": 512 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/3" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/4", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Ruby", "votes": 256 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/4" } ] } ] } + Response 201 (application/hal+json) { "_links": { "self": { "href": "/questions/1" } }, "_embedded": { "choices": [ { "_links": { "self": { "self": "/questions/1/choices/1" } }, "choice": "Swift", "votes": 2048 }, { "_links": { "self": { "self": "/questions/1/choices/2" } }, "choice": "Python", "votes": 1024 }, { "_links": { "self": { "self": "/questions/1/choices/3" } }, "choice": "Objective-C", "votes": 512 }, { "_links": { "self": { "self": "/questions/1/choices/4" } }, "choice": "Ruby", "votes": 256 } ] }, "published_at": "2014-11-11T08:40:51.620Z", "question": "Favourite programming language?" }

请求侧体现了 Request section 的用法:+ Request (application/json)携带媒体类型,紧接的缩进代码块即请求体。值得注意,+ question (string)、+ choices (array[string])这两个列表项写在动作描述与Relation之间,用于简要声明请求字段及其类型,与 Advanced Tutorial.md 中"用 MSON 的+ Attributes描述请求结构"的思路互补(本示例直接用内联字段声明,未引入完整 MSON 类型定义)。

响应侧则展示了一个语义细节:201 Created在 Siren 中以rel: [ "choices" ]内嵌选项、在 HAL 中以_embedded.choices表达,二者都向客户端宣告"新资源已可用,且可以立即投票"。

四、Group Question 资源组与 Question 资源详情

# Group Question Resources related to questions in the API. ## Question [/questions/{question_id}] A Question object has the following attributes: + question + published_at - An ISO8601 date when the question was published. + url + choices - An array of Choice objects. + Parameters + question_id: 1 (required, number) - ID of the Question in form of an integer

4.1 资源组

# Group Question是 Resource group section:由Group关键字 + 标识符定义,用于将相关资源组织在一起,可嵌套一个或多个资源节。这里的描述"Resources related to questions in the API"与普通版 Polls API 完全一致。

4.2 资源属性(Attributes 的简化写法)

+ question、+ published_at、+ url、+ choices这组列表项以"属性名 + 说明"的形式声明 Question 对象的字段(published_at标注为 ISO8601 日期,choices标注为 Choice 对象数组)。规范中 Attributes section 指出:资源节的属性代表资源数据结构,若资源带名称,这些属性可被其他Attributes节按名称引用;完整 MSON 语法可进一步给出类型、默认值与必填性(参见+ Attributes (object)及 Data Structures 节)。

4.3 路径参数 question_id

URI 模板/questions/{question_id}使用路径段变量(RFC 6570 Level 1 展开形式),配合参数节声明question_id为required, number。注意参数名的写法——大括号内的变量名question_id与参数节列表项的名称必须一致。规范还允许在动作级覆盖参数:资源级参数对所有嵌套动作生效,除非动作自身定义了 URI 模板。

4.4 View a Questions Detail [GET] 与双媒体响应

### View a Questions Detail [GET] + Relation: question + Response 200 (application/vnd.siren+json) { "actions": [ { "name": "delete", "href": "/questions/1", "method": "DELETE" } ], "properties": { "published_at": "2014-11-11T08:40:51.620Z", "question": "Favourite programming language?" }, "links": [ { "rel": [ "self" ], "href": "/questions/1" } ], "entities": [ { "actions": [ { "name": "vote", "href": "/questions/1/choices/1", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Swift", "votes": 2048 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/1" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/2", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Python", "votes": 1024 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/2" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/3", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Objective-C", "votes": 512 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/3" } ] }, { "actions": [ { "name": "vote", "href": "/questions/1/choices/4", "method": "POST" } ], "rel": [ "choices" ], "properties": { "choice": "Ruby", "votes": 256 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/4" } ] } ] } + Response 200 (application/hal+json) { "_links": { "self": { "href": "/questions/1" } }, "_embedded": { "choices": [ { "_links": { "self": { "self": "/questions/1/choices/1" } }, "choice": "Swift", "votes": 2048 }, { "_links": { "self": { "self": "/questions/1/choices/2" } }, "choice": "Python", "votes": 1024 }, { "_links": { "self": { "self": "/questions/1/choices/3" } }, "choice": "Objective-C", "votes": 512 }, { "_links": { "self": { "self": "/questions/1/choices/4" } }, "choice": "Ruby", "votes": 256 } ] }, "published_at": "2014-11-11T08:40:51.620Z", "question": "Favourite programming language?" }

+ Relation: question声明该动作的链接关系类型(link relation type)。依据规范 Relation section 与 Advanced Tutorial.md 的"Relation Types"章节:+ Relation: <标识符>为动作赋予领域语义,使客户端可以基于语义而非具体 URI构建——例如无论资源路径如何变化,"查看问题"始终是question关系。规范同时提醒:同一蓝图内每个资源的 relation 标识符应保持唯一。

五、Choice 资源:嵌套参数与投票动作

## Choice [/questions/{question_id}/choices/{choice_id}] + Parameters + question_id: 1 (required, number) - ID of the Question in form of an integer + choice_id: 1 (required, number) - ID of the Choice in form of an integer

5.1 双路径参数

URI 模板同时包含两个路径段变量,参数节依次声明question_id与choice_id,均为required, number。规范附录指出变量名仅允许字母、数字、_、百分号编码字符与.,且多个变量必须以逗号分隔、不得含空格。

5.2 View a Choice Detail [GET]

### View a Choice Detail [GET] + Relation: choice + Response 200 (application/vnd.siren+json) { "actions": [ { "name": "vote", "href": "/questions/1/choices/1", "method": "POST" } ], "rel": [ "choice" ], "properties": { "choice": "Swift", "votes": 2048 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/1" } ] } + Response 200 (application/hal+json) { "_links": { "self": { "href": "/questions/1/choices/1" } }, "choice": "Swift", "votes": 2048 }

单个选项的资源表示非常精简:Siren 版本携带vote动作与self链接,HAL 版本只保留_links与业务字段。这说明超媒体蓝图同样尊重"资源越小越清晰"的 REST 实践。

5.3 Vote on a Choice [POST]:展示状态变迁

### Vote on a Choice [POST] This action allows you to vote on a question's choice. + Relation: vote + Response 201 (application/vnd.siren+json) { "actions": [ { "name": "vote", "href": "/questions/1/choices/1", "method": "POST" } ], "rel": [ "choice" ], "properties": { "choice": "Swift", "votes": 2049 }, "links": [ { "rel": [ "self" ], "href": "/questions/1/choices/1" } ] } + Response 201 (application/hal+json) { "_links": { "self": "/questions/1/choices/1" }, "choice": "Swift", "votes": 2049 }

对比上文 GET 的响应体可以看到,投票后votes从2048变为2049——蓝图用相邻两个事务示例精确刻画了状态变迁。这正是 API Blueprint "文档即测试"特性的价值:解析器/测试工具可以直接用这两个示例校验真实 API 的行为是否符合预期(参见 examples/README.md 对"examples 目录中所有文件均为有效蓝图"的说明)。

六、与普通版 Polls API 的关键差异速查

维度Polls API(非超媒体)Polls Hypermedia API
媒体类型application/jsonapplication/vnd.siren+json、application/hal+json
导航方式响应体url字段 +Link头links/_links+rel关系
可执行操作客户端自行拼接 URLSirenactions(含method、fields)/ HAL 链接
内嵌资源普通 JSON 嵌套对象Sirenentities/ HAL_embedded
语义关系无显式关系声明每个动作带+ Relation:声明
创建响应201+Location头201+ 完整超媒体实体(含投票动作)

普通版的Retrieve the Entry Point返回{"questions_url": "/questions"},超媒体版则分别用 Sirenlinks与 HAL_links表达同一导航意图。两者共享相同的资源集合、Question 属性模型与 Choice 参数定义——区别只在于"链接与动作如何呈现"。

七、如何验证与消费这份蓝图

  1. 阅读源码:本仓库中 examples/Polls Hypermedia API.md 本身就是一份可解析的FORMAT: 1A文档;由于以.md命名,GitHub 会自动渲染(见 examples/README.md,建议以 raw 方式查看原稿)。
  2. 对照规范:逐节语法可对照 API Blueprint Specification.md(文档结构总览见其Blueprint document structure小节),进阶概念(Relation、Attributes、Data Structures)可参考 Advanced Tutorial.md 与 Tutorial.md。
  3. 生成文档与模拟:API Blueprint 生态工具(如 Apiary、各类开源解析器与模拟服务器)可直接消费此类蓝图,基于HOST元数据生成模拟端点;+ Relation:声明与双媒体类型响应可被客户端代码生成器与测试工具(如 Dredd)识别。
  4. 约定与限制:以本仓库为准,蓝图遵循 Format 1A(revision 9);媒体类型通过(application/...)标注在 Request/Response 定义行;同一动作下不同响应靠不同媒体类型形成独立事务示例。参数默认required、类型默认string,查询参数需用{?var}或{&var}形式写入 URI 模板。

八、结语

Polls Hypermedia API 示范了一条可复用的路线:资源模型保持不变,仅通过媒体类型选择与 Relation 声明,把 API 从"URL 手册"升级为"可导航的超媒体应用"。在 API Blueprint 中,这一切只需要在响应示例中忠实写出 Siren/HAL 结构,并为每个动作加上+ Relation:即可——蓝图既是人类可读的 Markdown 文档,又是机器可解析、可模拟、可测试的 API 契约。

  • 文档
  • API设计
  • 教程

【免费下载链接】api-blueprint

API Blueprint

项目地址:https://gitcode.com/gh_mirrors/ap/api-blueprint
点击查看免费下载

相关推荐

上一篇:开源项目Mini QR本地部署教程:Docker与Nginx配置实现私有化部署
下一篇:OpenJarvis 挖矿子系统扩展指南:基于 Provider 合约与 Sidecar 机制接入 Pearl 挖矿

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 7:09:38

AI小说生成器快速上手教程:如何自动生成多章节长篇并衔接上下文

AI小说生成器快速上手教程&#xff1a;如何自动生成多章节长篇并衔接上下文 【免费下载链接】AI_NovelGenerator 使用ai生成多章节的长篇小说&#xff0c;自动衔接上下文、伏笔 项目地址: https://gitcode.com/GitHub_Trending/ai/AI_NovelGenerator 写长篇有个绕不开的…

作者头像 李华
网站建设 2026/9/25 7:08:30

Atlas 300V 24G实战:YOLO模型迁移与推理性能调优全记录

身边好几个搞视觉的朋友最近都在问同一件事&#xff1a;昇腾的 Atlas 300V 24G 到底是不是一张运算加速卡&#xff0c;能不能用来跑 YOLO。我一开始还以为大家就是闲聊&#xff0c;结果发现是真有人拿着这块卡踩了一周的坑&#xff0c;最后连模型都没加载起来。说实话&#xff…

作者头像 李华
网站建设 2026/9/25 7:05:41

Atlas 300V 24G能否作为运算加速卡?YOLO模型部署实战解析

1. 整体设计与思路拆解1.1 先说结论&#xff1a;Atlas 300V到底是什么如果你最近在查AI推理加速相关的东西&#xff0c;大概率会碰到“Atlas”这个词。尤其是Atlas 300V 24G这款卡&#xff0c;很多人第一反应是&#xff1a;这货是不是类似RTX 4090那种显卡&#xff1f;能不能直…

作者头像 李华
网站建设 2026/9/25 7:04:41

PackML V2022在S7-1500上的工程化落地:状态驱动架构实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 7:04:29

Atlas 300V推理卡深度解析:从硬件认识到YOLO模型部署全流程实战

很多人一听“Atlas”第一反应是地图、是那个举着地球的肌肉男&#xff0c;但在AI圈子里&#xff0c;这个词这几年基本被华为的Atlas计算平台占了大半。热搜里那两个问题——“atlas部署yolo”和“atlas 300v 24g 是运算加速卡吗”&#xff0c;恰好点出了新人上手时最关心的两件…

作者头像 李华