- 文档
- API设计
- 教程
【免费下载链接】api-blueprint
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" } } }语法要点:
- 资源节(Resource section):由方括号内的 URI 模板 定义,形如
# <标识符> [<URI 模板>]。这里的标识符是Polls API Root。 - 动作节(Action section):由
## <标识符> [<HTTP 方法>]定义,这里是Retrieve the Entry Point [GET]。 - 双响应示例:同一个
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 return3.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 integer4.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 integer5.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/json | application/vnd.siren+json、application/hal+json |
| 导航方式 | 响应体url字段 +Link头 | links/_links+rel关系 |
| 可执行操作 | 客户端自行拼接 URL | Sirenactions(含method、fields)/ HAL 链接 |
| 内嵌资源 | 普通 JSON 嵌套对象 | Sirenentities/ HAL_embedded |
| 语义关系 | 无显式关系声明 | 每个动作带+ Relation:声明 |
| 创建响应 | 201+Location头 | 201+ 完整超媒体实体(含投票动作) |
普通版的Retrieve the Entry Point返回{"questions_url": "/questions"},超媒体版则分别用 Sirenlinks与 HAL_links表达同一导航意图。两者共享相同的资源集合、Question 属性模型与 Choice 参数定义——区别只在于"链接与动作如何呈现"。
七、如何验证与消费这份蓝图
- 阅读源码:本仓库中 examples/Polls Hypermedia API.md 本身就是一份可解析的
FORMAT: 1A文档;由于以.md命名,GitHub 会自动渲染(见 examples/README.md,建议以 raw 方式查看原稿)。 - 对照规范:逐节语法可对照 API Blueprint Specification.md(文档结构总览见其
Blueprint document structure小节),进阶概念(Relation、Attributes、Data Structures)可参考 Advanced Tutorial.md 与 Tutorial.md。 - 生成文档与模拟:API Blueprint 生态工具(如 Apiary、各类开源解析器与模拟服务器)可直接消费此类蓝图,基于
HOST元数据生成模拟端点;+ Relation:声明与双媒体类型响应可被客户端代码生成器与测试工具(如 Dredd)识别。 - 约定与限制:以本仓库为准,蓝图遵循 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
相关推荐
Simple Live:一站式跨平台直播聚合应用使用指南
Simple Live:一站式跨平台直播聚合应用使用指南 你是否厌倦了在多个直播应用之间来回切换?想要一个统一的平台来观看所有主流直播内容?Simple Liv
音视频直播球面卷积神经网络终极指南:如何在3D球形数据上构建等变深度学习模型
球面卷积神经网络终极指南:如何在3D球形数据上构建等变深度学习模型 球面卷积神经网络(S2CNN)是处理球形数据和3D旋转等变信号的前沿深度学习框架。这个开源项
API Blueprint实战:Polls API从规范到代码全流程
API Blueprint实战:Polls API从规范到代码全流程 你是否还在为API设计与开发的脱节而烦恼?是否经历过文档与实际接口不一致的尴尬?本文将通过
文档API设计教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考