はじめに
株式会社CAM のAdvent Calendar 10日目です。
昨日は @ohteru さんの「DIPについて勉強し直してみた」でした。
僕は最近Open APIやSwaggerに触れるようになったのですが、呼び方が違ったり、色々ツールがあったりでさっぱり理解できませんでした。そこで、公式のページをみていたのですが、公式は英語しかないのでちょっと辛いなと思い、自分が勉強した部分のみ翻訳をしてみました。
What is OpenAPI?
OpenAPI Specification(この前までのSwagger Specification)は REST APIの記述フォーマットです。
OpenAPI fileはAPIを以下の要素を含ませて定義できます。
- 利用可能なエンドポイント(/users)とその操作(GET /users, POST /users)
- それぞれのオペレーションの入力と出力
- 認証メソッド
- ライセンス、利用規約、問い合わせ先などの情報
API SpecificationはYAMLまたはJSONで記述できます。フォーマットは簡単で、機械にも人間にも読みやすいものになっています。
完全なOpenAPI Specificationは Github上で確認できます。
What is Swagger?
SwaggerとはOASに関連して作られ、設計したり、ビルドしたり、仕様書の作成を手助けしてくれるオープンソースツールの集まりのことです。
主なツールは
- Swagger Editor - OASを書くことができるブラウザベースのエディタです。
- Swagger UI - OASをインテラクティブなAPI仕様書として表示するツールです。
- Swagger Codegen - サーバのスタブやOASのクライアントのsdkを生成するツールです。
Why Use OpenAPI?
API構造を書き上げることはOpen APIにおけるはじめの一歩です。一度書けば、OASとSwagger toolsが様々な方法でAPI開発を手助けします。
- Swagger Codegenを用いてAPIサーバのstubを生成することができます。サーバーのロジックを追加するだけでstubを動かすことができます。
- Swagger Codegenでクライアントのライブラリを生成することができます。40以上の言語に対応しています。
- Swagger UIを用いることでドキュメントを生成することができ、ブラウザ上でAPIを試すことができます。
- specをAPI関連ツールに繋げることができます。例えば、specをSoupUIに取り入れることで、APIの自動テストを行うことができます。
- ほかのSwaggerと連携するツールもopen-sourceとcommercial toolsでチェックしましょう!
Basic Structure
OpenAPIはYAMLとJSON形式で記述できます。こちらではYAMLでの例を示しますが、JSONでも同様に動作します。
OpenAPI 3.0のYAMLでの定義はこのようになります。
openapi: 3.0.0
info:
title: Sample API
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.1.9
servers:
- url: http://api.example.com/v1
description: Optional server description, e.g. Main (production) server
- url: http://staging-api.example.com
description: Optional server description, e.g. Internal staging server for testing
paths:
/users:
get:
summary: Returns a list of users.
description: Optional extended description in CommonMark or HTML.
responses:
'200': # status code
description: A JSON array of user names
content:
application/json:
schema:
type: array
items:
type: string
すべてのキーワードは case-sensitive
です。
※ case-sensitive
とは
プログラムの大文字と小文字を区別数プログラムの性能のこと。
簡単に説明すると、RUNとrunを別のものとして識別します。
文字の大文字小文字を区別しないプログラミング言語はcase-insensitive
です。
Metadata
API定義は必ず OpenAPI Specificationのバージョンを含めなければなりません。
openapi: 3.0.0
OpenAPIバージョンは、何をどのようにしようとして書くかといったAPI定義の全体構造を決めます。
OpenAPI 3.0は semantic versioningという三つの数字を用いた表現方法を用います。
利用可能なバージョンは、3.0.0, 3.0.1, 3.0.2です。これらの機能はすべて同じです。
info
節には、title
, descrirption
, version
といったAPIの情報を含みます。
info:
title: Sample API
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.1.9
title
はAPIの名前、description
はAPIに関する追加の情報です。これはmultiline CommonMark
Servers
servers
節にはAPIサーバとbase URLを詳述します。一つまたは複数を定義できます。
例、本番環境と開発環境(sandbox)
servers:
- url: http://api.example.com/v1
description: Optional server description, e.g. Main (production) server
- url: http://staging-api.example.com
description: Optional server description, e.g. Internal staging server for testing
すべてAPIのパスはこのserverのURLに紐付きます。上記の例では、/users
は選択されているサーバによって
http://api.example.com/v1/users
または http://staging-api.example.com/users
となります。詳しい情報はこちらです。
Paths
paths
節ではAPI、HTTPメソッド(オペレーション)のそれぞれのエンドポイントを定義できます。例として、GET /users
は以下のように表現できます。
paths:
/users:
get:
summary: Returns a list of users.
description: Optional extended description in CommonMark or HTML
responses:
'200':
description: A JSON array of user names
content:
application/json:
schema:
type: array
items:
type: string
オペレーションの定義には、パラメータ、リクエストのbody、レスポンスのステータスコード(200 OK, 404 Not Found)などやレスポンスの内容が含まれます。詳しい情報は、Paths and Operationsをご確認ください。
Parameters
オペレーションはURLのpathを通して、あるいはクエリ文字列で、あるいはヘッダーやクッキーでパラメータを持ちます。
パラメータはデータ型、フォーマット、それが必須か任意かなどの詳細情報を定義できます。
paths:
/user/{userId}:
get:
summary: Returns a user by ID.
parameters:
- name: userId
in: path
required: true
description: Parameter description in CommonMark or HTML.
schema:
type : integer
format: int64
minimum: 1
responses:
'200':
description: OK
詳しく知りたい方はDescribe Parametersをご覧ください。
RequestBody
もしリクエストがbodyを送信する場合、requestBody
キーワードでbodyの内容とmediaタイプを設定してください。
paths:
/users:
post:
summary: Creates a user.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
username:
type: string
responses:
'201':
description: Created
追加情報は、Describing Request Bosyをご確認ください。
Responses
それぞれのHTTPメソッドにおいて、200 OKや404 Not Foundのステータスコードとレスポンスのbodyを定義できます。
schema
はそのまま中に書くか$ref
で参照するかで定義できます。それぞれの異なるcontentsのタイプに対して例を追加することができます。
paths:
/user/{userId}:
get:
summary: Returns a user by ID.
parameters:
- name: userId
in: path
required: true
description: The ID of the user to return.
schema:
type: integer
format: int64
minimum: 1
responses:
'200':
description: A user object.
content:
application/json:
schema:
type: object
properties:
id:
type: integer
format: int64
example: 4
name:
type: string
example: Jessica Smith
'400':
description: The specified user ID is invalid (not a number).
'404':
description: A user with the specified ID was not found.
default:
description: Unexpected error
レスポンスのHTTPステータスコードは"で"200"のように囲む必要があります(OpenAPI 2.0では囲む必要はありません)。
もっと知りたい方はこちらをご覧ください。
Input and Output Models
components/schemas
節にはAPI全体でつかわれるデータ構造を定義できます。これらは、パラメータやリクエストのbody、レスポンスのbodyにschema
が必要な時に$ref
によって追加する。
例えば、このJSONオブジェクトは
{
"id": 4,
"name": "Arthur Dent"
}
はこのように置き換えられます。
components:
schemas:
User:
properties:
id:
type: integer
name:
type: string
# Both properties are required
required:
- id
- name
そして、リクエストのbodyとレスポンスのbodyのschema
はこのように参照されます。
paths:
/users/{userId}:
get:
summary: Returns a user by ID.
parameters:
- in: path
name: userId
required: true
type: integer
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
/users:
post:
summary: Creates a new user.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/User'
responses:
'201':
description: Created
Authentication
securitySchemas
とsecurity
キーワードは、APIで使われている認証メソッドを表現します。
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
security:
- BasicAuth: []
サポートされている認証方法は以下の通りです。
- HTTP認証 Basic, Bearer, etc..
- クッキーやクエリ、ヘッダーへのAPIキー
- OAuth2
- OpenID Connect Discovery
そのほかの情報はこちらを確認ください。
Full Specification
すべてのOpenAPI 3.0 SpecificationはGithub上で確認できます。
https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md
Data Models(Schemas)
DataTypes
スキーマのデータ型は type
キーワードによって定義されます。例) type: string
OpenAPIは以下の型を定義します。
- string
- number
- integer
- boolean
- array
- object
これらの型は、呼び方は違うかもしれませんが、ほどんどのプログラミング言語に備えてあります。
Mixed Types, Numbers, Minimum and Maximumは省略します。
Strings
テキストの文字列はこのように定義されます。
type: string
文字列の長さは minLength
とmaxLength
を用いて制限できます。
type: string
minLength: 3
maxLength: 10
空の文字列はminLength
やpattern
を設定しない限り有効です。
String Formats
任意のformat
修飾子は文字列のフォーマットや中身についてのヒントを提供します。
- date - 日付表示 RFC 3339, section 5.6 例) 2017-07-01
- date-time - 時間表示 RFC 3339, section 5.6 例) 2017-07-21T17:32:28Z
- password -
- byte - base64-encoded 文字列 例) U3dhZ2dlciByb2Nrcw==
- binary -
しかし、format
はオープンな値なのでOASに定義されていない値であっても使用することができます。以下が例です。
- uuid
- uri
- hostname
- ipv4
- ipv6
- and others..
ツールはフォーマットを入力のバリデーションや、プログラミング言語の特定のタイプに値にするなどに使えます。特定のフォーマットをサポートしていないツールはformat
が設定されていない時と同じようにtype
のみを返します。
pattern
patternキーワードは文字列の正規表現テンプレートを定義できます。
このテンプレートに一致する値のみが受け付けられます。正規表現シンタックスはJavaScriptで使われているものです。正規表現はcase-sensitive、つまり大文字と小文字に区別があります。例えば、以下のパターンはSSN(123-45-6789)のフォーマットと一致します。
ssn:
type: string
pattern: '^\d{3}-\d{2}\d{4}$'
正規表現は^...$
によって囲まれること、そして^
は文字列の始め、$
は文字列の終わりを示します。もし ^..$
がない場合、部分一致として動作するので、正規表現として書かれている文字列を含むものが一致します。例えば、pattern:pet
はpet, petstoreやcarpetを一致とみなします。
Boolean, Null, Arrays, Objects, Files, Any Type, Authentucationは省略します。
Authentication and Authorization
Describing Security
セキュリティは securitySchemes
とsecurity
キーワードを使って表現します。securitySchemes
はAPIが提供するすべてのセキュリティスキーマを定義し、 security
で全てのAPIまたは一部のオペレーションに適用します。
Step 1. Defining securitySchemes
セキュリティスキーマはすべて components/securitySchemes
節で定義される必要があります。ここではセキュリティスキーマのtype
について説明します。
- http - Basic、BearerとHTTP認証スキーマ
- apiKey - APIキーとクッキー認証
- uauth2 - OAuth2
- openIdConnect - OpenID Connect Discovery
これに付随するプロパティはそれぞれのtype
に依存します。以下に示すのはそれぞれのセキュリティスキーマがどのように定義されるかを示します。 ここで、BasicAuth
, BearerAuth
などの名前はAPIの機能として追加するときに参照される任意の名前です。
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
BearerAuth:
type: http
scheme: bearer
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
OpenID:
type: openIdConnect
openIdConnectUrl: https://example.com/.well-known/openid-configuration
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://example.com/oauth/authorize
tokenUrl: https://example.com/oauth/token
scopes:
read: Grants read access
write: Grants write access
admin: Grants access to admin operations
Step 2. Applying security
securitySchemes
節でセキュリティスキーマについて定義した後は、それをAPIやその個々の操作にsecurity
節で追加することで適用することができます。rootレベルで適用する場合、security
節は設定された認証方法を、オペレーションレベルで上書きされない限りはすべてのAPIとそのオペレーションに適用されます。以下の例は、APIはAPIKeyまたはOauth2を用いて認証されます。 ApiKeyAuthとOAuth2という名前は上のsecuritySchemes
節で定義されたスキーマを参照します。
security:
- ApiKeyAuth: []
- OAuth2:
- read
- write
# The syntax is:
# - scheme name:
# - scope 1
# - scope 2
それぞれのスキーマに対して、APIを呼ぶのに必要な認証方法を指定できます。スコープはOAuth2とOpenID Connect Discoveryのために使われます。他のセキュリティスキーマを利用する場合、代わりに空の配列を使います。全体でのsecurity
節はそれぞれのオペレーションに対して違った認証を設定できます(違ったOAuth/OpenIDや、認証を外すことも可能です)。
paths:
/billing_info:
get:
summary: Gets the account billing info
security:
- OAuth2: [admin] # Use OAuth with a different scope
responses:
'200':
description: OK
'401':
description: Not authenticated
'403':
description: Access token does not have the required scope
/ping:
get:
summary: Checks if the server is running
security: [] # No security
responses:
'200':
description: Server is up and running
default:
description: Something is wrong
Scopes
OAuth 2とOpenID Connectはユーザリソースに対する許可を操作するためにスコープを利用します。 例えば、
Basic Authorization
API Keys
APIには認証にAPIキーを用いるものがあります。APIキーとは、クライアントがAPIを呼ぶときに作成したトークンです。キーは以下のようにクエリとして送信できたり、
GET /something?api_key=asbnaoirn024
また、ヘッダーとして
GET /something HTTP/1.1
X-API-Key: abcdef12345
あるいはクッキーとして送信できます。
GET /something HTTP/1.1
Cookie: X-API-KEY=abcdef12345
APIキーは、クライアントとserverしか知らない秘密情報とされています。Basic認証のように、APIキーによる認証はhttps/SSLのようなセキュリティのメカニズムと同じように安全な設計がされています。
Describing API Keys
OpenAPI 3.0では、以下のように定義されます。
openapi: 3.0.0
...
# 1) Define the key name and location
components:
securitySchemes:
ApiKeyAuth: # arbitrary name for the security scheme
type: apiKey
in: header # can be "header", "query" or "cookie"
name: X-API-KEY # name of the header, query parameter or cookie
# 2) Apply the API key globally to all operations
security:
- ApiKeyAuth: [] # use the same name as under securitySchemes
この例では、 リクエストのヘッダーとして送られるX-API-Key
という名前のAPIキーを定義します。キー名のApiKeyAuth
はセキュリティスキーマ用の名前です。ApiKeyAuth
という名前はAPIにセキュリティスキーマを設定するために、security
節で再度使われています。注意:securitySchemes
節だけに名前を設定するだけでは動作しません。APIキーとしての動作をさせるために、security
でも設定する必要があります。security
節は全体ではなく、操作(HTTPMethod)ごとに設定することができます。これは特定の操作のみAPIキーが必要な場合に便利です。
paths:
/something:
get:
# Operation-specific security:
security:
- ApiKeyAuth: []
responses:
'200':
description: OK (successfully authenticated)
一つのAPIで複数の認証をサポートすることもできます。 詳しくはこちらを確認ください。
Multiple API Keys
いくつかのAPIでは、API KeyとAppIDのように複数のセキュリティキーを組み合わせて使います。 組み合わせて使われることを明示するには、それらを同じ配列に並べてください。以下が例です。
components:
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-KEY
appId:
type: apiKey
in: header
name: X-APP-ID
security:
- apiKey: []
appId: [] # <-- no leading dash (-)
次のものとは別ものとして認識されます。
security:
- apiKey: []
- appId: []
これはどちらでのキーの認証も可能であることを示しています。詳しくは[こちら](https://swagger.io/docs/specification/authentication/#multiple)をご確認ください。
401 Response
401 "Unauthorized" レスポンスをAPIキーが違う、または見つからない場合に返すことができます。このレスポンスはWWW-Authenticate
ヘッダーを含みます。 他の共通レスポンスと同じように、401レスポンスは全体で利用可能なcomponents/responses
節で定義可能で、$ref
を用いて参照できます。
paths:
/something:
get:
...
responses:
...
'401':
$ref: "#/components/responses/UnauthorizedError"
post:
...
responses:
...
'401':
$ref: "#/components/responses/UnauthorizedError"
components:
responses:
UnauthorizedError:
description: API key is missing or invalid
headers:
WWW_Authenticate:
schema:
type: string
詳しく知りたい方は、こちらをご確認ください。
Bearer Authentication
Describing Bearer Authentication
401 Response
適切なBearerトークンを含んでいないリクエストに対し、レスポンスとして返すべき401 Unauthorizedを定義できます。これは複数の操作から呼ばれるであろうことから、components/responses
節に書き、
paths:
/something:
get:
...
responses:
'401':
$ref: '#/components/responses/UnauthorizedError'
...
post:
...
responses:
'401':
$ref: '#/components/responses/UnauthorizedError'
...
components:
responses:
UnauthorizedError:
description: Access token is missing or invalid
余談
YAMLファイルの拡張子について
こちらの記事より抜粋させていただきますと、
3文字拡張子と4文字拡張子
元々は 4文字の拡張子だったものが、Windows 95 の前身である MS-DOS や Windows 3.1 で拡張子を 3文字までしか扱えなかったことから、.html を .htm にしたり、.jpeg を .jpg にしたりなど、拡張子を無理矢理3文字に縮めたものが結構あります。その名残で、現在でも拡張子は 3文字以内にする例が多いようです。
公式?のYAMLに拡張子について質問がされており、理由など詳しい説明はありませんが、.yaml
の方でできれば用いてくれとの見解がありました。
https://yaml.org/faq.html
参考文献
https://swagger.io/docs/specification/about/
https://swagger.io/docs/specification/basic-structure/
https://yaml.org
いかがだったでしょうか?
学び始めは正直何を勉強すればいいのか分かりませんでしたが、自分が設定するべき項目をしっかりと調べて一つ一つ構築していくことで理解できるようになると思います。まだまだ学ぶべき機能がたくさんあるので、新しい機能を勉強するたびにこちらの記事に追記していこうと思います。
明日は @maruken24 さんの記事です、お楽しみに。