連結
連結是 OpenAPI 3.0 的新功能之一。使用連結,您可以描述一個操作傳回的各種值如何用作其他操作的輸入。這樣,連結會在操作之間提供已知的關係和遍歷機制。連結的概念有點類似於 超媒體,但 OpenAPI 連結不需要在實際回應中存在連結資訊。
何時使用連結?
考慮「建立使用者」操作
1POST /users HTTP/1.12Host: example.com3Content-Type: application/json4
5{6 "name": "Alex",7 "age": 278}9
10which returns the ID of the created user:11
12HTTP/1.1 201 Created13Content-Type: application/json14
15{16 "id": 30517}
然後可以使用此使用者 ID 來讀取、更新或刪除使用者:GET /users/305
、PATCH /users/305
和 DELETE /users/305
。使用連結,您可以指定「建立使用者」傳回的 id
值可以用作「取得使用者」、「更新使用者」和「刪除使用者」的參數。另一個範例是透過游標進行分頁,其中回應包含用於擷取下一個資料集的游標
1GET /items?limit=1002
3 ⇩4
5{6 "metadata": {7 "previous": null,8 "next": "Q1MjAwNz",9 "count": 1010 },11 ...12}13
14 ⇩15
16GET /items?cursor=Q1MjAwNz&limit=100
然而,連結關係不一定在相同的資源內,甚至不在相同的 API 規格內。
定義連結
連結在每個回應的 links
區段中定義
1responses:2 "200":3 description: Created4 content: ...5 links: # <----6 ...7 "400":8 description: Bad request9 content: ...10 links: # <----11 ...
為了更好地理解這一點,讓我們來看一個完整的範例。此 API 定義了「建立使用者」和「取得使用者」操作,「建立使用者」的結果用作「取得使用者」的輸入。
1openapi: 3.0.02info:3 version: 0.0.04 title: Links example5
6paths:7 /users:8 post:9 summary: Creates a user and returns the user ID10 operationId: createUser11 requestBody:12 required: true13 description: A JSON object that contains the user name and age.14 content:15 application/json:16 schema:17 $ref: "#/components/schemas/User"18 responses:19 "201":20 description: Created21 content:22 application/json:23 schema:24 type: object25 properties:26 id:27 type: integer28 format: int6429 description: ID of the created user.30 # -----------------------------------------------------31 # Links32 # -----------------------------------------------------33 links:34 GetUserByUserId: # <---- arbitrary name for the link35 operationId: getUser36 # or37 # operationRef: '#/paths/~1users~1{userId}/get'38 parameters:39 userId: "$response.body#/id"40
41 description: >42 The `id` value returned in the response can be used as43 the `userId` parameter in `GET /users/{userId}`.44 # -----------------------------------------------------45
46 /users/{userId}:47 get:48 summary: Gets a user by ID49 operationId: getUser50 parameters:51 - in: path52 name: userId53 required: true54 schema:55 type: integer56 format: int6457 responses:58 "200":59 description: A User object60 content:61 application/json:62 schema:63 $ref: "#/components/schemas/User"64
65components:66 schemas:67 User:68 type: object69 properties:70 id:71 type: integer72 format: int6473 readOnly: true74 name:75 type: string
links
區段包含具名的連結定義,在此範例中,只有一個名為 GetUserByUserId 的連結。連結名稱只能包含下列字元
1A..Z a..z 0..9 . _ -
每個連結都包含下列資訊
operationId
或operationRef
,指定目標操作。它可以是相同操作或目前或外部 API 規格中的不同操作。operationId
僅用於本機連結,而operationRef
可以連結到本機和外部操作。parameters
和/或requestBody
區段,指定要傳遞給目標操作的值。使用 執行階段運算式 語法,從父操作中擷取這些值。- (選用) 目標操作應使用的
server
,如果它與預設伺服器不同。 - (選用) 此連結的
description
。 CommonMark 語法可用於豐富文字表示。
此頁面的其餘部分將更詳細地介紹這些關鍵字。
operationId
如果目標操作已指定 operationId
,則連結可以指向此 ID,如上圖所示。此方法僅適用於本機連結,因為 operationId
值會在目前 API 規格的範圍內解析。
operationRef
當 operationId
不可用時,可以使用 operationRef
。operationRef
是使用 JSON 參考語法參考目標操作,與 $ref
關鍵字使用的語法相同。參考可以是本機 (在目前 API 規格內)
1operationRef: "#/paths/~1users~1{userId}/get"
或外部
1operationRef: 'https://anotherapi.com/openapi.yaml#/paths/~1users~1{userId}/get'2operationRef: './operations/getUser.yaml'
在此,字串 #/paths/~1users~1{userId}/get
實際上表示 #/paths//users/{userId}/get
,但路徑名稱中的內部斜線 / 需要逸出為 ~1
,因為它們是特殊字元。
1#/paths/~1users~1{userId}/get2 │ │ │3 │ │ │4paths: │ │5 /users/{userId}: │6 get: ─────────────────┘7 ...
此語法可能難以閱讀,因此我們建議僅將其用於外部連結。對於本機連結,將 operationId
指派給所有操作並連結到這些 ID 會更容易。
parameters 和 requestBody
連結最重要的部分是根據原始操作的值計算目標操作的輸入。這就是 parameters
和 requestBody
關鍵字的作用。
1links:2 # GET /users/{userId}3 GetUserByUserId:4 operationId: getUser5 parameters:6 userId: "$response.body#/id"7
8 # POST /users/{userId}/manager with the manager ID in the request body9 SetManagerId:10 operationId: setUserManager11 requestBody: "$response.body#/id"
語法為 _parameter_name: value_
或 requestBody: value
。參數名稱和請求主體是目標操作的名稱。無需列出所有參數,只需列出遵循連結所需的參數即可。同樣地,只有在目標操作有 主體 且連結的目的是定義主體內容時,才使用 requestBody
。如果兩個或更多參數具有相同的名稱,請使用參數位置 (path、query、header 或 cookie) 做為名稱的前置詞,如下所示
1parameters:2 path.id: ...3 query.id: ...
參數和 requestBody
的值可以使用以下方式定義
- 執行階段運算式,例如
$response.body#/id
,它會參考原始操作的請求或回應中的值, - 包含內嵌執行階段運算式的字串,例如
ID_{$response.body#/id}
, - 硬式編碼的值 (字串、數字、陣列等等),例如
mystring
或true
。
如果您需要為目標操作傳遞已評估和硬式編碼參數的特定組合,您通常會使用常數值。
1paths:2 /date_ranges:3 get:4 summary: Get relative date ranges for the report.5 responses:6 '200':7 description: OK8 content:9 application/json:10 example: [Today, Yesterday, LastWeek, ThisMonth]11 links:12 ReportRelDate:13 operationId: getReport14 # Call "getReport" with the `rdate` parameter and with empty `start_date` and `end_date`15 parameters:16 rdate: '$response.body#/1'17 start_date: ''18 end_date: ''19
20 # GET /report?rdate=...21 # GET /report?start_date=...&end_date=...22 /report:23 get:24 operationId: getReport25 ...
執行階段運算式語法
OpenAPI 執行階段運算式是從操作的請求和回應中擷取各種值的語法。連結使用執行階段運算式來指定要傳遞給連結操作的參數值。這些運算式稱為「執行階段」,因為這些值是從 API 呼叫的實際請求和回應中擷取的,而不是從 API 規格中提供的 範例值 中擷取的。下表描述了執行階段運算式語法。所有運算式都參考定義 links
的目前操作。
運算式
描述
$url
完整的請求 URL,包括查詢字串。
$method
請求 HTTP 方法,例如 GET 或 POST。
$request.query._param_name_
指定查詢參數的值。該參數必須在操作的 parameters
區段中定義,否則無法評估。參數名稱區分大小寫。
$request.path._param_name_
指定路徑參數的值。該參數必須在操作的 parameters
區段中定義,否則無法評估。參數名稱區分大小寫。
$request.header._header_name_
指定請求標頭的值。此標頭必須在操作的 parameters
區段中定義,否則無法評估。標頭名稱不區分大小寫。
$request.body
整個請求主體。
$request.body_#/foo/bar_
請求主體中由 JSON 指標指定的部分。
$statusCode
回應的 HTTP 狀態碼。例如,200 或 404。
$response.header._header_name_
指定的回應標頭的完整值,以字串形式表示。標頭名稱不區分大小寫。該標頭不需要在回應的 headers
區段中定義。
$response.body
整個回應主體。
$response.body_#/foo/bar_
請求主體中由 JSON 指標指定的部分。
foo{$request.path.id}bar
將運算式放入 {}
大括號中,以便將其嵌入到字串中。
注意事項
- 評估後的運算式具有與參考值相同的類型,除非另有說明。
- 如果無法評估執行階段運算式,則不會將任何參數值傳遞給目標操作。
範例
考慮以下請求和回應
1GET /users?limit=2&total=true2Host: api.example.com3Accept: application/json
1HTTP/1.1 200 OK2Content-Type: application/json3X-Total-Count: 374
5{6 "prev_offset": 0,7 "next_offset": 2,8 "users": [9 {"id": 1, "name": "Alice"},10 {"id": 2, "name": "Bob"}11 ]12}
以下是一些執行階段運算式的範例以及它們評估後的值
運算式 | 結果 | 註解 |
---|---|---|
$url | http://api.example.com/users?limit=2&total=true | |
$method | GET | |
$request.query.total | true | total 必須定義為查詢參數。 |
$statusCode | 200 | |
$response.header.x-total-count | 37 | 假設 X-Total-Count 定義為回應標頭。標頭名稱不區分大小寫。 |
$response.body#/next_offset | 2 | |
$response.body#/users/0 | {"id": 1, "name": "Alice"} | JSON 指標(#/... 部分)使用以 0 為基礎的索引來存取陣列元素。但是沒有萬用字元語法,因此 $response.body#/users/*/id 無效。 |
$response.body#/users/1 | {"id": 2, "name": "Bob"} | |
$response.body#/users/1/name | Bob | |
ID_{$response.body#/users/1/id} | ID_2 |
伺服器
預設情況下,目標操作會針對其預設的 伺服器 呼叫 – 全域 servers
或操作特定的 servers
。但是,可以使用 server
關鍵字覆寫連結的伺服器。server
具有與全域伺服器相同的欄位,但它是一個單一伺服器,而不是陣列。
1servers:2 - url: https://api.example.com3---4links:5 GetUserByUserId:6 operationId: getUser7 parameters:8 userId: "$response.body#/id"9 server:10 url: https://new-api.example.com/v2
重複使用連結
連結可以內嵌定義(如先前的範例所示),或放置在全域 components/links
區段中,並透過 $ref
從操作的 links
區段中參考。如果多個操作以相同的方式連結到另一個操作,這可能會很有用 – 參考有助於減少程式碼重複。在以下範例中,「建立使用者」和「更新使用者」操作都會在回應主體中傳回使用者 ID,此 ID 用於「取得使用者」操作。來源操作會重複使用 components/links
中的相同連結定義。
1paths:2 /users:3 post:4 summary: Create a user5 operationId: createUser6 ...7 responses:8 '201':9 description: Created10 content:11 application/json:12 schema:13 type: object14 properties:15 id:16 type: integer17 format: int6418 description: ID of the created user.19 links:20 GetUserByUserId:21 $ref: '#/components/links/GetUserByUserId' # <-------22
23 /user/{userId}:24 patch:25 summary: Update user26 operationId: updateUser27 ...28 responses:29 '200':30 description: The updated user object31 content:32 application/json:33 schema:34 $ref: '#/components/schemas/User'35 links:36 GetUserByUserId:37 $ref: '#/components/links/GetUserByUserId' # <-------38
39 get:40 summary: Get a user by ID41 operationId: getUser42 ...43
44components:45 links:46 GetUserByUserId: # <----- The $ref's above point here47 description: >48 The `id` value returned in the response can be used as49 the `userId` parameter in `GET /users/{userId}`.50 operationId: getUser51 parameters:52 userId: '$response.body#/id'