概要
JSONを返すAPI実装するときに、以下のように共通部分でラップしたレスポンスを返すことがある。
{ "header": { "code": "0", "message": "success" }, "result": { "books": [ { "id": 1, "name": "技術書1", "price": 3000 }, { "id": 2, "name": "技術書2", "price": 1500 } ] } }
※ 「共通部分でラップ」とは以下の部分のこと
"header": { "code": "0", "message": "success" }, "result": { # ここに返したいデータを入れる }
実装する側になったときに共通部分のことを考えるのが地味に面倒なのと、そもそもこれって要るのかな?って前から気になっていたので調べてみた。
「共通部分」の呼び方について
『Web API The Good Parts』によると、この共通部分のことは「エンベロープ(=封筒)」と呼ぶらしい。
エンベロープとは日本語で言うと「封筒」を意味しますが、APIのデータ構造の文脈で言えば、すべてのデータ(レスポンスやリクエスト)を同じ構造でくるむことを言います。
...
メタデータも含んだような形ですべてのAPIが同じデータ構造を前すために実際のデータをくるむための構造をエンベロープと呼びます。
『Web API The Good Parts』 p78〜p79より
たしかに、「API エンベロープ」とか「API envelope」で検索すると情報がたくさん出てくる。
そもそもAPIの共通部分のことを「エンベロープ」って呼ぶっていう知識がないとこの手の議論にアクセスできないんだなと思った。(私は今まで知らなかった。)
「エンベロープ」は必要なのか
『Web API The Good Parts』には「エンベロープ」はメタ情報を扱えるので一見便利に見えるが、以下の理由でやるべきではないと記載されていた。
言われてみれば、以下のようにHTTPステータスコードに応じてレスポンスを返せば不要な気がする。
200のとき
{ "books": [ { "id": 1, "name": "技術書1", "price": 3000 }, { "id": 2, "name": "技術書2", "price": 1500 } ] }
400のとき
{ "code": "400-1", "message": "invalid parameter xxx." }
※ 200系の場合と400系や500系の場合でJSONスキーマが異なると、クライアント側でJSONをデシリアライズするときに困るのでは?と思うかもしれないが、クライアント側がそれぞれのステータスコードの場合のJSONスキーマを知っていれば問題ない。
参照系以外のAPIの場合はどうするのか?
次に気になったのは参照系(=CRUDのR以外)の場合はどうするのかということだった。
必要がなければ何も返す必要がないのだが、APIでレスポンスボディに何も返さないというのは、個人的には感覚として気持ち悪い気もする。
調べてみたところ、例えば以下の記事ではC(作成)については記述がなかったが、
- U(更新): 更新後のオブジェクトを返す。
- D(削除):
204 No Content
を返す。
ことを提案していてなるほどと思った。
また、RFCも読んでみた。
https://datatracker.ietf.org/doc/html/rfc2616#section-9.5
RFC2616によると、以下が推奨されていた。
POST
で新規作成した場合:201 Created
を返して作成されたリソースを参照できるもの(恐らく主キーのなど)を返す。PUT
で更新した場合:200
か204 No Content
を返す。DELETE
で削除した場合:200
か202 Accepted
(削除処理が非同期の場合)か204 No Content
を返す。
自分がAPIを実装するときは、正常なときは固定で200
を返してしまうことが多くて、201 Created
とか202 Accepted
とか204 No Content
とか200系のHTTPステータスコードを細かく使い分けたことがなかったので、なるほどと思った。
いったん以上