信息架构、超媒体设计与客户端弹性提升
立即解锁
发布时间: 2025-08-14 01:02:07 阅读量: 4 订阅数: 18 


RESTful Web API设计与实践精华
### 超媒体信息架构与客户端弹性提升
#### 1. 信息架构概述
Dan Klyn 有一个名为 “Explaining Information Architecture” 的精彩短视频,展示了本体论、分类法和编排如何共同构成信息架构模型。
#### 2. 超媒体与 “先验设计”
从 “设计” 主题开始探讨信息系统,是因为设计信息系统从一开始就会确立一些规则。设计方案能让信息系统决策的基础变为现实,这是一种 “先验设计” 方法,即提前规划系统元素。采用这种方法的优势在于能定义系统的稳定元素,以此构建服务并实现交互。
设计方法需要一个适用于多种解决方案的模型。例如,仅适用于内容管理系统(CMS)而不适用于客户关系管理系统(CRM)的设计方法并非好方法。不同解决方案在设计和技术层面有很多共同点,但需要梳理出这些相似点形成一套设计原则。
当创建随时间变化的解决方案时,需要一个既能提供稳定性又能支持变化的基础设计元素,在这些设计中,超媒体(链接和表单)就是这样的元素。Fielding 称超媒体为 “应用状态的引擎”,它能实现 Licklider 所提到的元消息传递,也能实现 Kay 的 “极端延迟绑定”。
#### 3. 提升超媒体客户端的弹性
API 服务需要稳定可预测,而 API 客户端应用需要具备适应性和弹性。客户端应用有其特定任务和目的,明确目的以及创建 API 消费者时的明确程度很重要。
详细的 API 客户端指令会使客户端在特定任务上有效,但会降低其可重用性。若目标服务有重大变化,客户端应用可能会 “崩溃”。因此,要平衡 API 的可用性和可重用性,抽象化有助于提高重用性,如 HTTP 协议虽抽象但可重用性高,但需要大量支持技术、标准和工具。
提升客户端应用弹性的方法聚焦于以下四个重要特征:
- 关注协议和格式
- 在运行时解决交互细节
- 为机器对机器(M2M)交互进行不同设计
- 依赖客户端和服务器共享的语义词汇
##### 3.1 绑定协议和格式
构建成功的超媒体客户端应用的重要元素是将客户端与响应绑定。编写 API 消费者应用时,会在生产者(服务)和消费者(客户端)之间创建绑定,“绑定代理” 是客户端和服务器共享的内容,有效的绑定应很少随时间变化。
常见的绑定目标如 URL(如 /persons/123)或对象(如 {id:"123", {person:{...}}}),很多框架和生成器使用它们自动生成客户端应用的静态代码,但这种应用难重用且易崩溃。
对于 Web API,协议(如 HTTP、MQTT 等)和消息格式(如 HTML、Collection+JSON 等)是更好的绑定目标,它们比 URL 和对象更稳定,是更高层次的抽象,更通用且不易变化。例如,绑定到 Collection+JSON 格式的客户端应用可用于支持该格式绑定的任何服务,就像 HTML 浏览器 30 多年来所做的那样。
##### 3.2 运行时元数据解析
创建灵活的 API 客户端的挑战之一是处理每个 HTTP 请求的细节,如使用的 HTTP 方法、URL 参数、请求体数据和 HTTP 头元数据等。
处理请求元数据的典型方法是将其 “嵌入” 服务接口,但这种方式会使客户端应用在 URL 或对象/参数变化时崩溃,尤其是在 API/服务生命周期早期,变化频繁。
避免这种情况的方法是让客户端应用在运行时识别和遵循元数据中的请求细节。HTML 浏览器多年来一直这样做,例如以下 HTML 表单就是运行时元数据的示例:
```html
<form action="/persons/" method="post",
enc-type="application/x-www-form-urlencoded">
<input type="text" name="givenName" value="" required />
<input type="text" name="familyName" value="" required />
<input type="tel" name="telephone" value="" required />
<input type="email" name="email" value="" required />
<input type="submit" />
</form>
```
除了 HTML,还有一些基于 JSON 的格式也支持丰富的运行时元数据,如 Collection+JSON、SIREN 和 UBER。
支持运行时元数据使编写人机应用变得容易,但支持 M2M 交互则更复杂,因为缺少人类大脑的参与。
#### 4. 机器对机器交互的挑战
人类大脑在填写 HTML 表单时能完成很多复杂任务:
- 识别可填写和提交的表单
- 确定需要提供的四个输入项
- 理解 givenName 等值的含义
- 从记忆或其他来源查找值填充所有输入项
- 知道按下提交按钮将数据发送到服务器处理
- 处理错误消息和服务器响应
编写人机 API 客户端时可假设人类智能 “免费” 可用,但 M2M 应用缺少这种智能。可以选择编写机器学习和人工智能程序,也可以采用另一种方法,即让客户端应用具备 “有限智能”,依靠媒体类型处理表单及其元数据,利用语义配置文件处理表单中的参数并关联本地数据。
#### 5. 依赖语义词汇
目前,Web 上最成功的 M2M 交互大多是只读数据的交互,如网络蜘蛛和搜索机器人。M2M 交互的挑战部分在于单个请求的数据属性,人类拥有丰富的数据,而机器通常没有。为表单添加新字段对人类来说不是大问题,但对 M2M 客户端应用可能是 “重大变化”,需要客户端和服务器双方努力克服。
应对这一挑战的有效方法是依靠语义配置文件,它详细列出一组问题(如账户管理、支付服务等)中可能出现的属性名称和操作细节(链接和表单),为客户端应用预期理解的词汇设定边界。客户端和服务器可提前就成功交互所需的数据属性达成一致。通过使用语义配置文件,能获得另一个重要的 “绑定代理”,适用于 M2M 交互,现在可以将协议、格式和配置文件作为客户端 - 服务器交互的三个稳定且灵活的绑定元素。
#### 6. 支持以客户端为中心的工作流
大多数 API 客户端应用静态绑定到单个 API 服务,本质上是一次性定制的。这些应用与服务的绑定方式体现在工作流实现上,常见的是使用顺序工作流作为绑定代理,客户端应用只知道一组可能的工作流且细节不变。
例如,一个名为 customerOnboarding 的服务可能提供以下 URL 和对象模式:
- /onboarding/customer 模式为 { customer: {…}}
- /onboarding/contact 模式为 { contact: {…}}
- /onboarding/agreement 模式为 { agreement: {…}}
- /onboarding/review 模式为 { review: {…}}
服务有四个按顺序执行的步骤,通常在服务文档中说明,客户端应用需要将文档中的说明转换为代码,如下所示:
```javascript
function onboardCustomer(customer, contact, agreement, review) {
http.send("/onboarding/customer","POST", customer);
http.send("/onboarding/contact", "POST", contact);
http.send("/onboarding/agreement", "POST", agreement);
http.send("/onboarding/review", "POST", review);
return "200 OK";
}
```
这种静态绑定的问题是,服务工作流的任何变化(如添加 creditCheck 步骤)都会使客户端应用 “崩溃”。
更好的方法是告知客户端需要完成的工作,并让客户端应用能够选择和执行出现的步骤。可以使用响应中的超媒体解决这个问题,示例代码如下:
```javascript
function onboardCustomer() {
results = http.read("/onboarding/work-in-progress", "GET");
while(results.actions) {
var action = results.actions.pop();
http.send(action.url, action.method, map(action.parameters,local.data));
}
return "200 OK";
}
```
在这个简化示例中,客户端向服务 “询问” 要执行的操作列表,然后使用消息中的运行时元数据执行这些操作,直到没有更多操作要完成。这种方式在工作流顺序或步骤数量变化时,客户端实现更不容易崩溃。
还有一种以客户端为中心的工作流方式,即客户端设置工作流而非服务。例如,客户端需要借助多个独立服务完成任务时,服务之间相互不知晓,工作流由客户端应用控制,真正的 “应用” 仅以客户端实现的形式存在。伪代码如下:
```javascript
function onboardCustomer(choreography) {
while(choreography) {
var step = choreography.pop();
var action = http.get(step.URL);
http.send(action.url, action.method, map(action.parameters,local.data));
}
return "200 OK";
}
```
在这个示例中,工作流编排来自客户端应用,客户端应用决定何时完成工作,这是一种以客户端为中心的目标设定方式,在有稳定灵活的绑定元素(协议、格式和配置文件)基础上较易实现。另外,还有一种基于一个或多个服务器上特定状态值的客户端中心工作流方法。
### 超媒体信息架构与客户端弹性提升
#### 7. 总结与对比
为了更清晰地理解不同设计方法和工作流模式的特点,下面通过表格进行对比总结:
| 设计元素/模式 | 特点 | 优势 | 劣势 | 适用场景 |
| --- | --- | --- | --- | --- |
| 传统绑定(URL 和对象) | 直接绑定特定 URL 和对象模式 | 快速部署客户端应用 | 难重用,易因服务变化而崩溃 | 短期、稳定服务 |
| 协议和格式绑定 | 绑定协议(如 HTTP)和消息格式(如 Collection+JSON) | 稳定、通用,可用于多种服务 | 需要一定技术支持 | 长期、多变服务 |
| 静态工作流 | 客户端应用固定顺序执行服务步骤 | 实现简单 | 服务工作流变化时易崩溃 | 服务工作流稳定的场景 |
| 动态工作流(服务提供步骤) | 客户端根据服务提供的元数据执行步骤 | 适应服务工作流变化 | 对服务元数据要求高 | 服务工作流可能变化的场景 |
| 客户端中心工作流(客户端设置步骤) | 客户端控制多个服务的工作流 | 灵活,可组合多个服务 | 实现复杂 | 需要整合多个独立服务的场景 |
#### 8. 实现步骤与流程
以下是实现以客户端为中心的动态工作流的详细步骤和 mermaid 流程图:
##### 8.1 实现步骤
1. **服务端准备**
- 服务端提供包含工作流元数据的接口,如 `/onboarding/work-in-progress`,返回当前可执行的操作列表。
- 每个操作包含 URL、HTTP 方法、参数等信息。
2. **客户端编写**
- 客户端通过 `GET` 请求获取工作流元数据。
- 解析元数据,根据操作信息执行相应的 HTTP 请求。
- 循环执行操作,直到没有更多操作。
##### 8.2 mermaid 流程图
```mermaid
graph TD;
A[客户端启动] --> B[获取工作流元数据];
B --> C{是否有操作};
C -- 是 --> D[执行操作];
D --> E[更新工作流元数据];
E --> C;
C -- 否 --> F[工作完成];
```
#### 9. 代码示例详解
下面对前面提到的代码示例进行详细解释:
##### 9.1 静态工作流代码
```javascript
function onboardCustomer(customer, contact, agreement, review) {
http.send("/onboarding/customer","POST", customer);
http.send("/onboarding/contact", "POST", contact);
http.send("/onboarding/agreement", "POST", agreement);
http.send("/onboarding/review", "POST", review);
return "200 OK";
}
```
- 此代码中,客户端应用按照固定顺序依次向四个 URL 发送 `POST` 请求,传递相应的数据。
- 优点是实现简单,缺点是服务工作流变化时,如添加或删除步骤,客户端代码需要修改。
##### 9.2 动态工作流代码
```javascript
function onboardCustomer() {
results = http.read("/onboarding/work-in-progress", "GET");
while(results.actions) {
var action = results.actions.pop();
http.send(action.url, action.method, map(action.parameters,local.data));
}
return "200 OK";
}
```
- 客户端首先通过 `GET` 请求获取工作流元数据。
- 然后使用 `while` 循环遍历元数据中的操作列表,依次执行每个操作。
- 这种方式能适应服务工作流的变化,因为客户端根据服务提供的元数据动态执行操作。
##### 9.3 客户端中心工作流代码
```javascript
function onboardCustomer(choreography) {
while(choreography) {
var step = choreography.pop();
var action = http.get(step.URL);
http.send(action.url, action.method, map(action.parameters,local.data));
}
return "200 OK";
}
```
- 客户端从 `choreography` 中获取工作流步骤。
- 依次执行每个步骤,通过 `GET` 请求获取操作信息,然后执行相应的 HTTP 请求。
- 这种方式适用于需要整合多个独立服务的场景,客户端可以灵活控制工作流。
#### 10. 实践建议
在实际应用中,为了更好地实现超媒体信息架构和提升客户端弹性,可参考以下建议:
1. **选择合适的绑定元素**
- 根据服务的稳定性和可变性,选择协议、格式和语义配置文件作为绑定元素,避免使用 URL 和对象等易变的绑定方式。
2. **支持运行时元数据**
- 服务端提供详细的运行时元数据,客户端应用能够识别和遵循这些元数据,以适应服务的变化。
3. **设计灵活的工作流**
- 优先考虑动态工作流和客户端中心工作流,使客户端应用能够适应服务工作流的变化,提高可重用性和弹性。
4. **使用语义配置文件**
- 在 M2M 交互中,使用语义配置文件为客户端和服务器提供统一的词汇和操作规范,降低交互难度。
通过以上方法和实践建议,可以构建出更稳定、灵活和可重用的信息系统,提升客户端应用的弹性和适应性,满足不断变化的业务需求。
0
0
复制全文
相关推荐









