Swagger3.0(OpenAPI Specification 3.0)で強化されたParameter Object

## 背景

[2017/7にSwaggerはOpenAPI Specificationとして生まれ変わりました。](https://swagger.io/announcing-openapi-3-0/)
正確にはSwaggerの開発元(?)である[SMATRBEAR Software](https://smartbear.com/)がSwagger2.0を[Open API Initiative](https://www.openapis.org/)に寄贈し、それを元にOpenAPI Specification（以下OAS） 3.0が作られました。

Swagger2.0と比較してOAS3.0では様々な拡張とリファクタリングが行われましたが、その中でParameter Objectについて記法がかなり拡張されたのでそれについて書きたいと思います。

## そもそもSwagger(OAS)って何？

[こちらの記事](http://qiita.com/NewGyu/items/64adc3c5dd3ab8ad6087)でも述べましたが、この時と多少変わっているので、 https://swagger.io/specification/ から引用します。

> The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.

要するにはRESTfulAPIのインターフェース定義を記述するためのものです。

昨今では[銀行がAPIを公開し始めて](http://jp.techcrunch.com/2017/03/31/banks-mfcloud-api/)いたりします。API利用者に対してAPIの使い方を説明する仕様書を提示することになると思いますが、その仕様書の書き方に一石を投じることになるでしょう。
従来は仕様書はExcelやWordや色々な文書として書かれていましたが、OASが整備されたことによって統一化されていくことが期待されます。

具体的にはYAMLもしくはJSONで記述します。こうした機械的に扱いやすい形式を採用することで、

* HTMLやPDF形式のドキュメントとして出力
* APIクライアントのソースコード生成
* API実装の雛形生成

などを自動化することが出来ます。

事実、SMARTBEARは[Swagger Tools](https://swagger.io/tools/)というツール群を提供しています。

## Parameter Object

[Parameter Object](https://swagger.io/specification/#parameterObject)は下記のようなAPIエンドポイントの入力引数を表すオブジェクトです。

* GET /user/{username}
* GET /pet/findByStatus?status=Living

### Parameterの場所(in)

一般的にはクエリーとしてパラメータを定義することが多いでしょうが、OASでは`in`フィールドで、

* query
* path
* header
* cookie

の４種類が指定できます。
尚、OAS3.0からリクエストボディは[Request Body Object](https://swagger.io/specification/#request-body-object-55)としてParameterとは別の概念になったため、`in:body`と言う指定方法は廃止されました。

### defaultの廃止

Swagger2.0まではdefaultとして、パラメータのデフォルト値を指定できましたが廃止されました。

### schema

Swagger2.0まではschema定義できるのは`in`が`body`の時（すなわちリクエストボディを定義するとき）だけでしたが、OAS3.0では`in`の種類によらずSchema定義が可能です。Schemaのtypeとしてobjectを指定することも可能で、つまりは単純な文字、配列だけでなく複雑な階層化されたJSONオブジェクトをクエリパラメータやヘッダとして指定することが可能になっています。

また、Swagger2.0まではParameterの型がstring,integer,booleanの場合には直接Parameter Objectにtypeを指定していたのですが廃止され、schema定義をする必要があります。

### style

上記の`schema.type`としてarrayやobjectが指定することができるようになったのとに伴いOAS3.0からParameterのシリアライズという概念が導入されました。

> Describes how the parameter value will be serialized 

シリアライズは端的に言うと`JSON object`や`JSON array`を文字列化することです。

これは、Swagger2.0ではcollectionFormatという指定でtypeがarrayのときのみでしたが、OAS3.0からはstyleに変更され、typeがobjectの場合の考慮もされるようにました。
しかし、その反面、どんなときにどのstyleが使用できると言うルールが複雑化しました。

JSONの文字列化なら[stringify](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)すればいいじゃないかって？ 私もそう思うのですが、単純にstringifyするとURLセーフではない文字列が含まれうるということでしょう。
（それにしてもパーセントエンコードすれば良いのではと思うのですが・・・）

### explode

trueの場合、object, arrayを別個のパラメータに分割します。
これは例を見たほうが早いでしょう。

```yaml:swagger定義
/hair:
  parameters:
    - name: color
      in: query
      schema:
        type: object
      style: form
```

```json:colorの具体値例
{"R": 0, "G": 255, "B": 128}
```

この場合、explodeの値により以下のようになります。

|explode|クエリ式|
|-------|-----------------|
|false  |/hair?color=R,0,G,255,B,128|
|true   |/hair?R=0&G=255&B=255|

この形式はstyleによって変わります。

## style, explodeのまとめ

従来のcollectionFormatに代わって登場したstyleですが、

* inの種類によって使用可能なstyleに制約がある
* explodeとの組み合わせでシリアライズ形式が変る
* typeによってもシリアライズ形式が変る

ということでかなり仕様としては複雑になりました。

### pathに指定可能なstyle

`/path/to/{param}`の場合

|style |explode   ||
|------|---------|-------------------------------|
|matrix|false  |/path/to/;color=R,100,G,200,B,150 |
|      |true |/path/to/;R=100;G=200;B=150       |
|label |false  |/path/to/.R.100.G.200.B.150       |
|      |true |/path/to/.R=100.G=200.B=150       |
|simple|false  |/path/to/R,100,G.200,B,150       |
|      |true |/path/to/R=100,G=200,B=150       |

[RFC6570](https://tools.ietf.org/html/rfc6570)

### queryに指定可能なstyle

`/path/to?param=`の場合

|style |explode   ||
|------|---------|-------------------------------|
|form |false     |/path/to?param=R,100,G,200,B,150|
|     |true      |/path/to?R=100&G=200&B=150     |
|spaceDelimited|false|/path/to?param=R%20100%20G%20200%20B%20150|
|pipeDelimited |false|/path/to?param=R&brvbar;100&brvbar;G&brvbar;200&brvbar;B&brvbar;150|
|deepObject    |true |/path/to?color[R]=100&color[G]=200&color[B]=150|

ここで戸惑うのは`collectionFormat=csv`相当の`style=simple`が使用できなくなったことです。

### headerに指定可能なstyle

|style |explode   | |
|------|---------|-------------------------------|
|simple|false  |x-param: R,100,G.200,B,150       |
|      |true   |x-param: R=100,G=200,B=150       |

※explode=trueのとき自信なし

### cookieに指定可能なstyle

|style |explode   | |
|------|---------|-------------------------------|
|form |false     |cookie: param=R,100,G,200,B,150|
|     |true      |cookie: R=100; G=200; B=150;   |

※explode=trueのとき自信なし

## まとめてみたものの

### まだ仕様的に整合が取れていない気がする

https://swagger.io/specification/#styleValues によると

* spaceDelimited
* pipeDelimited
* simple

は type=array のみではないのか？ 

また、in=cookie, style=form, explode=true の場合は&ではなく;で区切るべきと思われる。

### SwaggerUI側が未対応

Parameter Objectを細かく記述したとして、残念ながらSwagger UIがまだ対応していないためドキュメントには反映されません。