Contents

OpenAPI Spec 撰寫小記

最近規劃 API 規格打算用 OpenAPI Spec(OAS),但真的不是很熟,所以打算文件刷一遍,這邊做個小記。

OpenAPI Specification v3.1.0 | Introduction, Definitions, & More

OpenAPI Specification (OAS)

什麼是 OpenAPI Specificatio,簡稱 OAS

  1. 一個定義 HTTP APIs 簡易規劃介面敘述語言。
  2. 可以讓電腦再不看原始碼情況下和解析網路封包下可以知道 API 怎麼使用
  3. 當通過OpenAPI正確定義時,消費者可以以最少的實現邏輯來理解並與遠程服務進行交互。類似於接口描述為低級別編程所做的工作,OpenAPI規範刪除了調用服務時的猜測。

價值

  1. 生產文件工具
  2. 生產程式
  3. 生產測試工具

定義

OpenAPI Document

MUST
一個OpenAPI 文件必需包含

  1. 至少 paths field
  2. 至少a components field or a webhooks field

當然必須符合這些規範。

Path Templating

Path Templating 是用大括號{}的template expressions來展示,URL中的路徑(URL Path)可替換成參數(URL Paramter)

MUST
每個 template expression必需包含path parameter和 Path ItemPath Item’s Operations

path item為空的話,ACL constraints, matching path parameters為非必要。 ==>這句理解不能,但我猜是指下圖空直。
https://i.imgur.com/BcAkeos.png

MUST NOT
path parameters 不能包含符號。如[RFC3986]: forward slashes (/), question marks (?), or hashes (#).

Media Types

媒體類型定義分佈在幾個資源中。

SHOULD
the media type 定義應該遵從 [RFC6838]規範.

text/plain; charset=utf-8
application/json
application/vnd.github+json
application/vnd.github.v3+json
application/vnd.github.v3.raw+json
application/vnd.github.v3.text+json
application/vnd.github.v3.html+json
application/vnd.github.v3.full+json
application/vnd.github.v3.diff
application/vnd.github.v3.patch

003-RFC关于媒体类型说明 - bjlhx15 - 博客园

HTTP Status Codes

Hypertext Transfer Protocol (HTTP) Status Code Registry

規範

Version

這邊指 OpenAPI 版本,這邊大至上有看沒懂。感覺就像是大版本小版本之類相容問題,Swagger 版本需要放三碼,

‘3.0.*’ 這個也可以用。但Swagger 工具不能用,我就不深入琢磨。

格式

所有 key 區分大小寫。

兩種 Field
Fixed fields, which have a declared name, and Patterned fields, which declare a regex pattern for the field name.

  • 固定欄位
  • 花樣欄位

RECOMMENDED
為了讓JSON和YAML格式來回切換,我們推薦

YAML - Failsafe Schema - 極客書

MUST
YAML Tag 必須最低要求符合JSON規則。ref:YAML Ain’t Markup Language (YAML™) Version 1.2
YAML地圖中使用的鍵必須僅限於標量字符串,如YAML Failsafe Schema規則集所定義。YAML Ain’t Markup Language (YAML™) Version 1.2

NOTE: 我們定義 API 使用 OpenAPI 文件可以是 YAML 或 JSON格式。但是 API Request和 Response 的Body不一定是要 JSON 或 YAML。

文件結構

MAY
OpenAPI 文件可以由一個和多個、分段個文件,這部分由編寫者決定。在這種情況下,Reference ObjectSchema Object$ref 可以被使用。

RECOMMENDED
通常 OpenAPI document 的根文件命名使用 openapi.jsonor openapi.yaml.

資料型態

OAS 的資料型態基於 JSON Schema Specification Draft 2020-12,請注意,整數(Integer)作為類型也得到了支持,並將其定義為沒有分數或指數部分的JSON號。使用Schema Object定義模型,該對像是JSON Schema規範2020-12的超集。

如JSON Schema驗證詞彙所定義的draft-bhutton-json-schema-validation-00,資料類型可以具有可選的修飾符屬性

https://i.imgur.com/SemCjTL.png

注意NumberInteger差異。Number是含小數點數字。可以看Numeric types — Understanding JSON Schema 2020-12 documentation

Rich Text Formatting

在整個規範中,description字段被認為是支持Commark Markdown格式的支持。
MUST
如果OpenAPI Tooling呈現豐富的文本,則必須至少支持CommonMark 0.27所述的降級語法。
MAY
工具可以選擇忽略一些共同標記功能來解決安全問題。

Relative References in URIs

MAY
除非另有說明,否則所有 URIs 的屬性都可以是[RFC3986]定義的相對引用。

這邊不知道 URL和 URI是什麼可以看一下URL和URI傻傻分不清,差別到底在哪裡?

相對引用,包括在reference-objectpath-item-object$ref欄位。link-objectexternalValue根據[RFC3986],使用引用文檔作為基本URI解決。

如果URI包含片段標識符,則應根據引用文檔的片段分辨率來解析片段。如果引用文檔的表示為JSON或YAML,則根據[RFC6901],應將片段標識符解釋為 JSON-Pointer。

JSON Pointer 不知道可以看Overview of JSON Pointer | Baeldung,感覺跟 JSON Path 有點像。也可以看這篇JSON Pointer

正如JSON模式規范草案2020-12所述,Schema Objects中的相對引用,包括任何以$id值出現的引用,使用最近的父$id作為基URI。如果沒有包含$id的父模式,則必須根据[RFC3986]确定基URI。

jsonschema - ‘$id’ property usage in JSON Schema - Stack Overflow
JSON Schema 规范(中文版)

不過我看文件沒有$id範例,所以這邊就不先深入探討。

Relative References in URLs

除非另有說明,否則所有屬性URL的屬性可能是[RFC3986]定義的相對引用。除非另有說明,否則使用Service Object 中定義為基本 URL解決相對引用。請注意,這些本身可能與引用文檔有關。

Schema

在下面的描述中,如果一個字段沒有顯式的REQUIRED或用MUST或SHALL描述,它可以被認為是OPTIONAL。

以下真的太多內容了,所以我覺得不重要的就不會記到這邊。

OpenAPI Object

REQUIRED

  • openapi(string) OAS 版本
  • info(Info Object) 資訊
  • paths([Paths Object]) The available paths and operations for the API.

OPTIONAL

  • components([Components Object]) 可寫定義元件
  • security([Security Requirement Object]) API 安全用
  • tags([Tag Object]) 分類用
  • externalDocs(External Documentation Object) 額外文件
  • servers([Server Object]) Sever 資訊

webhooks 3.1.0才有的東西,目前不知道有什麼特別作用。

Info Object

該對象提供有關API的元數據。如果需要,客戶可以使用元數據,並可以在編輯或文檔生成工具中呈現。

REQUIRED

  • title(string)
  • version(string)

OPTIONAL

  • summary(string)
  • description(string)
  • contact(Contact Object)
  • license(License Object)

Server Object

REQUIRED

  • url(string) 可以用誇號字段{brackets}搭配variablesfield來搭配變數使用。

OPTIONAL

  • description
  • variables(Map[string, Server Variable Object]) 可以搭配 url 變數使用

This object MAY be extended with Specification Extensions.
可以使用Specification Extensions作擴展

範例
1
2
3
4
5
6
7
8

servers:
- url: https://development.gigantic-server.com/v1
  description: Development server
- url: https://staging.gigantic-server.com/v1
  description: Staging server
- url: https://api.gigantic-server.com/v1
  description: Production server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17

servers:
- url: https://{username}.gigantic-server.com:{port}/{basePath}
  description: The production API server
  variables:
    username:
      # note! no enum here means it is an open value
      default: demo
      description: this value is assigned by the service provider, in this example `gigantic-server.com`
    port:
      enum:
        - '8443'
        - '443'
      default: '8443'
    basePath:
      # open meaning there is the opportunity to use special base paths as assigned by the provider, default is `v2`
      default: v2

Server Variable Object

REQUIRED

  • default(string) If the enum is defined, the value MUST exist in the enum’s values

OPTIONAL

  • enum
  • description

Components Object

  • schemas (Map[string, Schema Object])
  • responses (Map[string, Response Object | Reference Object])
  • parameters (Map[string, Parameter Object | Reference Object])
  • examples (Map[string, Example Object | Reference Object])
  • requestBodies (Map[string, Request Body Object | Reference Object])
  • headers (Map[string, Header Object | Reference Object])
  • securitySchemes (Map[string, Security Scheme Object | Reference Object])
  • links (Map[string, Link Object | Reference Object])
  • callbacks (Map[string, Callback Object | Reference Object])
  • pathItems (Map[string, Path Item Object | Reference Object])

This object MAY be extended with Specification Extensions.

All the fixed fields declared above are objects that MUST use keys that match the regular expression: ^[a-zA-Z0-9.-_]+$.

Paths Object

  • /{path} (Path Item Object)

必須要/開頭。規則上面Path Templating有提到

This object MAY be extended with Specification Extensions.

Paths Object Example

Path Item Object

  • $ref (string)
  • summary (string)
  • description (string)
  • get (Operation Object)
  • put (Operation Object)
  • post (Operation Object)
  • delete (Operation Object)
  • options (Operation Object)
  • head (Operation Object)
  • patch (Operation Object)
  • trace (Operation Object)
  • servers ([Server Object])
  • parameters ([Parameter Object | Reference Object])

這邊文件有特別說明 Paramters

  1. 這些參數可以在操作級別上覆蓋,但不能在此處刪除。
  2. 該列表不得包含重複的參數。 MUST NOT
  3. 唯一參數由namelocation的組合定義。
    這邊名稱是指這個(name)、location是指這個(in)
  4. 該列表可以使用參考對象鏈接到 OpenAPI Object’s components/parameters的參數。

Operation Object

Operation Object
這邊比較意外沒有必填寫項目。
我把我覺得重要的簡單寫一下。先前寫過事項就不再寫了

  • operationId (string)
    用于標識操作的惟一字符串。id在API中描述的所有操作中必須是唯一的。operationId區分大小寫。工具和庫可以使用operationId來唯一標識一個操作,因此,建議遵循通用的編程命名約定。
  • tags
  • summary
  • description
  • requestBody(Request Body Object | Reference Object)
    在其他情況下,HTTP規格模糊(例如GET,HEAD和DELETE),請求機體被允許但沒有明確定義的語義,應避免使用。 ==>這邊我是認為 POST時候可能就會用到這個
  • responses (Responses Object)
    從執行此操作返回的可能responses的列表。
  • deprecated (boolean)
    聲明此操作要棄用。消費者應避免使用聲明的操作。默認值為false。
  • security ([Security Requirement Object])
  • servers ([Server Object])

Parameter Object

這邊注意用的參數有四種類型,請看下面。

Parameter Locations

There are four possible parameter locations specified by the in field:

  • path - Used together with Path Templating, where the parameter value is actually part of the operation’s URL. This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
  • query - Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
  • header - Custom headers that are expected as part of the request. Note that [RFC7230] states header names are case insensitive.
  • cookie - Used to pass a specific cookie value to the API.

看似應該沒有BODY內容,這邊需要注意一下。

Fixed Fields

REQUIRED

  • name (string)
    區分大小寫
  • in (string) 只有四種"query", “header”, “path” or “cookie”

OPTIONAL

  • description
  • required paramter location(in) 選擇 path 就必須預設值為true。但是其他的話預設為 false
  • deprecated
  • allowEmptyValue

這邊特別紀錄

Style

一般我們知道很多後端傳送值都有自己的樣式,這邊可以特別調整。這邊我就不詳細記錄

Style Values

https://i.imgur.com/VRmrRi8.png

範例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

name: token
in: header
description: token to be passed as a header
required: true
schema:
  type: array
  items:
    type: integer
    format: int64
style: simple

Request Body Object

REQUIRED

  • content (Map[string, Media Type Object]) e.g. text/plain overrides text/*

OPTIONAL

  • description (string)
  • required (boolean) Defaults to false.
範例
1
2
3
4
5
6
7
8
9

description: user to add to the system
required: true
content:
  text/plain:
    schema:
      type: array
      items:
        type: string

Media Type Object

  • schema (Schema Object)
  • example (Any)
    example object 應為Media Type Objec指定的正確格式
    if referencing a schema which contains an example, the examples value SHALL override the example provided by the schema.(example object會把 $ref覆蓋掉)
    examples字段與example字段互斥(看似應該擇一填寫?)
  • examples (Map[ string, Example Object | Reference Object])
  • encoding (Map[string, Encoding Object])
Considerations for File Uploads

上傳檔案

1
2
3
4

# a PNG image as a binary file:
content:
    image/png: {}
1
2
3
# an arbitrary binary file:
content:
    application/octet-stream: {}

推薦用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

requestBody:
  content:
    multipart/form-data:
      schema:
        properties:
          # The property name 'file' will be used for all files.
          file:
            type: array
            items: {}
Support for x-www-form-urlencoded Request Bodies (POST常用)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
requestBody:
  content:
    application/x-www-form-urlencoded:
      schema:
        type: object
        properties:
          id:
            type: string
            format: uuid
          address:
            # complex types are stringified to support RFC 1866
            type: object
            properties: {}

Responses Object

OpenAPI Specification v3.1.0 | Introduction, Definitions, & More

Fixed Fields
  • default (Response Object | Reference Object)
Patterned Fields
  • HTTP Status Code (Response Object | Reference Object)
範例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

'200':
  description: a pet to be returned
  content: 
    application/json:
      schema:
        $ref: '#/components/schemas/Pet'
default:
  description: Unexpected error
  content:
    application/json:
      schema:
        $ref: '#/components/schemas/ErrorModel'

Response Object

This object MAY be extended with Specification Extensions.

Fixed Fields

REQUIRED

  • description (string) 第一次看到敘述必填。

OPTIONAL

  • headers (Map[string, Header Object | Reference Object]) 這邊不能設定"Content-Type" header
  • content (Map[string, Media Type Object])
  • links (Map[string, Link Object | Reference Object])
範例
1
2
3
4
5
6
7
8

description: A complex object array response
content: 
  application/json:
    schema: 
      type: array
      items:
        $ref: '#/components/schemas/VeryComplexType'

純文字

1
2
3
4
5
6

description: A simple string response
content:
  text/plain:
    schema:
      type: string

with header

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20

description: A simple string response
content:
  text/plain:
    schema:
      type: string
    example: 'whoa!'
headers:
  X-Rate-Limit-Limit:
    description: The number of allowed requests in the current period
    schema:
      type: integer
  X-Rate-Limit-Remaining:
    description: The number of remaining requests in the current period
    schema:
      type: integer
  X-Rate-Limit-Reset:
    description: The number of seconds left in the current period
    schema:
      type: integer

沒會應東西

1
2

description: object created

Example Object

  • summary (string)
  • description (string)
  • value (Any)
  • externalValue (string) The value field and externalValue field則一填寫
範例

In a request body:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
requestBody:
  content:
    'application/json':
      schema:
        $ref: '#/components/schemas/Address'
      examples: 
        foo:
          summary: A foo example
          value: {"foo": "bar"}
        bar:
          summary: A bar example
          value: {"bar": "baz"}
    'application/xml':
      examples: 
        xmlExample:
          summary: This is an example in XML
          externalValue: 'https://example.org/examples/address-example.xml'
    'text/plain':
      examples:
        textExample: 
          summary: This is a text example
          externalValue: 'https://foo.bar/examples/address-example.txt'

In a parameter:

1
2
3
4
5
6
7
8
9
parameters:
  - name: 'zipCode'
    in: 'query'
    schema:
      type: 'string'
      format: 'zip-code'
    examples:
      zip-example: 
        $ref: '#/components/examples/zip-example'

In a response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
responses:
  '200':
    description: your car appointment has been booked
    content: 
      application/json:
        schema:
          $ref: '#/components/schemas/SuccessResponse'
        examples:
          confirmation-success:
            $ref: '#/components/examples/confirmation-success'

先不深入研究,好像也很重要。

OpenAPI Specification v3.1.0 | Introduction, Definitions, & More

Header Object

OpenAPI Specification v3.1.0 | Introduction, Definitions, & More

跟 Paramter Object 一樣,但不能寫inname
第一次看這個會覺得怎麼不能寫name很奇怪,但上層是Map[string,Header Object]所以就這麼單純。

範例
1
2
3
4

description: The number of allowed requests in the current period
schema:
  type: integer

Tag Object

Fixed Fields

REQUIRED
name

OPTIONAL
description

1
2
3

name: pet
description: Pets operations

Reference Object

REQUIRED

  • $ref (string) URI

OPTIONAL

  • summary (string)
  • description (string)
範例
1
2

$ref: '#/components/schemas/Pet'

Schema Object

我覺得這邊很玄學。就先跳過這章節,我覺得看範例可以很快知道要怎麼用。
好像規格定義在裡面https://spec.openapis.org/oas/3.1/dialect/base

官方範例

雖然我跳過,但這個我還是覺得很重要。

Discriminator Object

看起來是分辨物件用。

The discriminator object is legal only when using one of the composite keywords oneOf, anyOf, allOf.

Discriminator Object 僅在使用一個複合關鍵字之一 oneOf, anyOf, allOf時才是合法的。

Fixed Field

REQUIRED

  • propertyName (string) 定義 type 範例

OPTIONAL

  • mapping (Map[string, string]) 定義 type 名稱指定schema

這邊推薦看 line-bot-sdk-java/DemographicFilter.java at cbcb096023f2de84684751839656a0c458f6ee41 · line/line-bot-sdk-java 他們實作才看得懂這邊在做什麼?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
JsonTypeInfo(use = Id.NAME, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(GenderDemographicFilter.class),
        @JsonSubTypes.Type(AgeDemographicFilter.class),
        @JsonSubTypes.Type(AppTypeDemographicFilter.class),
        @JsonSubTypes.Type(AreaDemographicFilter.class),
        @JsonSubTypes.Type(SubscriptionPeriodDemographicFilter.class),
        @JsonSubTypes.Type(OperatorDemographicFilter.class)
})
public interface DemographicFilter {
}

這部分支援還要看有沒有找到相關方法去解決這段處理。

範例
1
2
3
4
5
6
7
8

MyResponseType:
  oneOf:
  - $ref: '#/components/schemas/Cat'
  - $ref: '#/components/schemas/Dog'
  - $ref: '#/components/schemas/Lizard'
  discriminator:
    propertyName: petType

Security Scheme Object

https://i.imgur.com/ez4MbLT.png

  • type => “apiKey”, “http”, “mutualTLS”, “oauth2”, “openIdConnect”.

我覺得這章節看範例比較快。

範例

OpenAPI Specification v3.1.0 | Introduction, Definitions, & More

彩蛋

如何利用 OpenAPI 及 WebHooks 讓老舊的網路服務也可程式化

[筆記] Postman 常見的 Content-type. 使用 Postman 來測試 Restful API 是相當方便的,但對於… | by R. H. | hobo engineer | Medium