回调
在 OpenAPI 3 规范中,您可以定义回调 - 异步的、带外的请求,您的服务将在响应某些事件时发送到其他服务。 这有助于您改进 API 向客户端提供的工作流程。 回调的一个典型示例是订阅功能 – 用户订阅您服务的某些事件,并在发生此事件或彼事件时收到通知。 例如,网上商店可以在每次购买时向经理发送通知。 这些通知将是“带外的”,也就是说,它们将通过与访问者工作的连接不同的连接进行,并且它们将是异步的,因为它们将超出常规的请求-响应流程。 在 OpenAPI 3 中,您可以定义“订阅”操作的格式以及回调消息的格式和对这些消息的预期响应。 此描述将简化不同服务器之间的通信,并帮助您标准化 API 中 Webhook 的使用。
回调示例
让我们创建一个回调定义 - 一个简单的 Webhook 通知。 假设您的 API 提供一个 POST /subscribe
操作,该操作期望在请求体中包含回调 URL
1POST /subscribe2Host: my.example.com3Content-Type: application/json4
5{6 "callbackUrl": "https://myserver.com/send/callback/here"7}
API 确认订阅 —
1HTTP/1.1 201 Created
— 稍后在某些事件上发送通知
1POST /send/callback/here2Host: myserver.com3Content-Type: application/json4
5{6 "message": "Something happened"7}
现在让我们定义 /subscribe
操作
1openapi: 3.0.02info:3 version: 0.0.04 title: test5
6paths:7 /subscribe:8 post:9 summary: Subscribe to a webhook10 requestBody:11 required: true12 content:13 application/json:14 schema:15 type: object16 properties:17 callbackUrl: # Callback URL18 type: string19 format: uri20 example: https://myserver.com/send/callback/here21 required:22 - callbackUrl23 responses:24 "201":25 description: Webhook created
现在让我们向此操作添加回调关键字以定义回调
1paths:2 /subscribe:3 post:4 summary: Subscribe to a webhook5 requestBody: …6 callbacks: # Callback definition7 myEvent: # Event name8 "{$request.body#/callbackUrl}": # The callback URL,9 # Refers to the passed URL10 post:11 requestBody: # Contents of the callback message12 required: true13 content:14 application/json:15 schema:16 type: object17 properties:18 message:19 type: string20 example: Some event happened21 required:22 - message23 responses: # Expected responses to the callback message24 "200":25 description: Your server returns this code if it accepts the callback
让我们逐行浏览此定义
callbacks
在相关操作内定义 -post
,put
等(不在路径本身下)。 在此示例中,在/subscribe
路径的post
方法下
1paths:2 /subscribe:3 post:4 …5 callbacks:6 …
这并不意味着 API 仅在此操作工作时发送回调。 您的 API 将在您的服务业务逻辑需要时发送回调请求。 关键字的层次结构只是让您使用 /subscribe
操作的参数来配置回调请求(请参阅下文)。
- 在
callbacks
中,我们定义一个或多个回调消息。 在我们的示例中,我们只有一个消息。 您可以在下面找到一个包含多个回调的示例。 每个回调的定义都以事件名称开头(在我们的示例中为_myEvent_
)
1callbacks:2 myEvent: # Event name
- 在事件名称下,我们定义您的服务将向其发送回调消息的 URL。 在我们的示例中,URL 通过
{$request.body#/callbackUrl}
表达式指定
1callbacks:2 myEvent:3 "{$request.body#/callbackUrl}": # The callback URL, refers to the URL passed in the request body
此表达式表示回调 URL 将基于 /subscribe
操作的参数。 我们稍后将详细介绍这些表达式。
- 在 URL 下方,我们指定回调消息的方法,并定义消息格式和预期响应。 这些定义与常规请求和响应定义类似
1callbacks:2 myEvent:3 "{$request.body#/callbackUrl}":4 post: # Method5 requestBody: # Contents of the callback message6 …7 responses: # Expected responses8 …
请注意,当您定义回调时,您正在定义 API 的规范。 回调功能的实际实现是在服务器代码中完成的。
使用运行时表达式引用请求字段
如您所见,我们在示例中使用了 {$request.body#/callbackUrl}
表达式。 它是运行时表达式,它设置将在回调中使用哪个 POST /subscribe
请求的数据。 运行时 意味着与 API 端点不同,此 URL 不是预先知道的,而是根据 API 客户端提供的数据在运行时评估的。 此值因客户端而异。 例如,POST /subscribe
请求可以如下所示
1POST /subscribe?p1=query-param-value HTTP/1.12Host: my.example.com3Content-Type: application/json4Content-Length: 1875
6{7 "callbackUrl" : "http://my.client.com/callback"8}9
10201 Created11Location: http://my.example.com?id=123
您可以使用以下表达式来引用其数据
表达式 | 示例 | 描述 |
---|---|---|
{$url} | /subscribe | 父操作 URL。 |
{$method} | POST | 回调请求的方法。 |
{$request.path.eventType} | myEvent | 事件名称。 |
{$request.query.param-name} | query-param-value (p1 查询参数) | 指定查询参数的值。 |
{$request.header.header-name} | application/json (Content-Type 标头) | “订阅”请求的指定标头。 |
{$request.body#/field-name} | callbackUrl | 请求体中的字段。 如果该字段是数组,请使用类似 {$request.body#/arrayField/2} 的语法。 |
{$response.header.header-name} | http://my.example.com?id=123 (Location 标头) | 指定响应标头的值(对“订阅”请求的响应)。 |
您可以在回调定义中将运行时表达式与静态数据组合。 例如,您可以通过以下方式定义回调 URL
1{$request.body#callbackUrl}/data:2– or –3{$request.body#/callbackUrl}/{$request.query.eventType}:
您可以使用表达式来指定查询参数
1{$request.body#/callbackUrl}/data?p1={$request.query.eventType}
如果字符串同时包含运行时表达式和静态文本,则应将运行时表达式括在花括号中。 如果整个字符串是运行时表达式,则可以跳过花括号。
多个回调
如上所述,您可以使用一个“订阅”操作来定义多个回调
1/subscribe:2 post:3 requestBody:4 content:5 application/json:6 schema:7 type: object8 properties:9 inProgressUrl:10 type: string11 failedUrl:12 type: string13 successUrl:14 type: string15 responses:16 "200":17 description: OK18 callbacks:19 inProgress:20 "{$request.body#/inProgressUrl}":21 post:22 requestBody:23 $ref: "#/components/requestBodies/callbackMessage1"24 responses:25 "200":26 description: OK27 "{$request.body#/failedUrl}":28 post:29 requestBody:30 $ref: "#/components/requestBodies/callbackMessage2"31 responses:32 "200":33 description: OK34 "{$request.body#/successUrl}":35 post:36 requestBody:37 $ref: "#/components/requestBodies/callbackMessage3"38 responses:39 "200":40 description: OK
取消订阅回调
您如何实现取消订阅机制取决于您。 例如,接收服务器可以在响应回调消息时返回特定代码,以指示它不再对回调感兴趣。 在这种情况下,客户端只能在响应回调请求时取消订阅。 要允许客户端随时取消订阅,您的 API 可以提供一个特殊的“取消订阅”操作。 这是一种相当常见的方法。 在这种情况下,您的服务可以为每个订阅者生成一个 ID 或令牌,并在响应“订阅”请求时返回此 ID 或令牌。 要取消订阅,客户端可以将此 ID 传递给“取消订阅”操作,以指定要删除的订阅者。 以下示例演示了如何在规范中定义此行为
1paths:2/subscribe:3 description: Add a subscriber4 post:5 parameters:6 - name: callbackUrl7 in: query8 required: true9 schema:10 type: string11 format: uri12 - name: event13 in: query14 required: true15 schema:16 type: string17 responses:18 '201':19 description: Added20 content:21 application/json:22 type: object23 properties:24 subscriberId:25 type: string26 example: AAA-123-BBB-45627 links: # Link the returned id with the unsubscribe operation28 unsubscribeOp:29 operationId: unsubscribeOperation30 parameters:31 Id: $response.body#/subscriberId32 callbacks:33 myEvent:34 '{$request.query.callbackUrl}?event={$request.query.event}':35 post:36 requestBody:37 content:38 application/json:39 example:40 message: Some event41 responses:42 '200':43 description: OK44
45/unsubscribe:46 post:47 operationId: unsubscribeOperation48 parameters:49 - name: Id50 in: query51 required: true52 schema:53 type: string