回调
在 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.42info: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
现在让我们向此操作添加 `callbacks` 关键字以定义回调
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