Web API The Good Partsを読んで改めて気づいたことやメモしておきたいことなどをまとめました。
目次
エンベロープを使うべきか
エンベロープとは日本語で言うと「封筒」を意味します。APIデータ構造の文脈で言えば、以下のように全てのデータを同じ構造でくるむことを言います。
1 2 3 4 5 6 7 8 9 10 | { "header": { "status": "success", "errorCode": 0 }, "response": { "id": "1", ... } } |
上記の例では、headerとresponseという2つのデータを保つ構造をしており、headerにはステータスなどのメタデータを入れ、responseには実際のデータが格納されています。
エンベロープは、こうしたメタデータも含みデータをくるむような構造をしています。
Web APIは基本的にHTTPがエンベロープの役割を果たしているから使う必要なし
メタデータなどをレスポンスに含めることで、クライアントサイドが扱いやすくなると言うメリットはあるものの、このエンベロープは冗長な表現であるため、基本的には使うべきではありません。
なぜならHTTPにはヘッダの概念があり、そこにステータスコードなどのメタデータを入れて返すことができ、わざわざレスポンスにまで含めると冗長化するからです。
また、エラーの場合でもステータスコードが200番になっていたりすると、処理が成功したのか失敗したのか判別できなくなります。
エラーの詳細をクライアントに返すときはレスポンスボディに格納することも
エラーの詳細を返す方法は大きく2つあり、1つ目はHTTPのレスポンスヘッドに入れて返す方法、2つ目はレスポンスボディにいれて返す方法があります。
1つ目のレスポンスヘッダに入れる方法は、以下のように独自に定義したヘッダ項目を用意して格納します。
1 2 3 | X-MYNAME-ERROR-CODE: 2013 X-MYNAME-ERROR-MESSAGE: Bad authentication token X-MYNAME-ERROR-INFO: http://api.example.com/v1/auth |
2つ目のレスポンスボディに入れる方法は、以下のようにJSONのレスポンスボディとして、エラーのデータ構造を用意して格納します。
1 2 3 4 5 6 7 | { “error”: { “code”: 2013, “message”: “Bad authentication token”, “info”: “http://api.example.com/v1/auth” } } |
先程のエンベロープの話からすると、エラー情報はヘッダに入れた方が良い気がしますが、実際に公開されている多くのAPIがボディにエラーメッセージを格納する方法を採用しています。
複数のエラーが同時に発生した場合など、ボディに配列を用意してそこにエラーを格納することができるため、このようなエラーの場合などは、ヘッダに独自の項目を用意するより、ボディで対応した方が良い場合もあります。
APIをバージョンで管理する
バージョン番号の付け方
バージョニングのルールとして広く知られている手法にセマンティックバージョニングがあります。
セマンティックバージョニングでは、バージョンは基本的には”1.2.3”のように3つの数値をドットで繋いだものになっています。それぞれ数値は先頭からメジャー、マイナー、パッチと呼ばれています。
- メジャー:後方互換性のない変更が行われた場合に増える
- マイナー:後方互換性のある機能変更、あるいは特定の機能が今後廃止されることが決まった場合に増える
- パッチ:ソフトウェアのAPIに変更がないバグ修正などを行った場合に増える
APIの場合は、複数のAPIバージョンのメンテナンスやユーザからの利便性の観点からも、頻繁にバージョンを上げるべきではないため、メジャーバージョンで表すのが適切といえます。
以下のようなURIのパスにバージョンを入れ、メジャーバージョンで管理するのが一般的です。
1 | https://api.example.com/v1 |
メディアタイプの指定
HTTPのリクエスト、レスポンスでは送信するデータ本体の形式を表すためにメディアタイプを指定する必要があります。メディアタイプはデータ形式のことで、レスポンスデータがJSONなのかHTMLなのか、画像なのかなどを示しています。
リクエストの場合は、クライアントがどのようなメディアタイプを理解できるか指定することができます。
レスポンスの場合は、以下のようにヘッダのContent-Typeを利用してメディアタイプを指定します。
1 | Content-Type: application/json |
・MIME タイプの一覧
Content-Typeで指定する必要性
正しくデータを読み出せない場合がある
クライアントの多くは、まずはContent-Typeの値を使ってデータ形式を判断しており、その指定を間違えると正しくデータを読み出すことができない場合があります。
例えばAFNetworkingというライブラリでは、application/json、text/json、text/javascriptのメディアタイプのみ受け入れており、それ以外はエラーになります。
このようにメディアタイプが厳格である場合には、Content-Typeを正確に指定しないとエラーに悩まされることになります。
セキュリティ上の問題を引き起こす
実際のレスポンスデータと異なるメディアタイプをContent-Typeすることで、セキュリティ上の問題を引き起こすこともあります。
例えば、JSONファイルを誤ってtext/htmlで配信してしまった場合、データ通信を実現するXMLHttpRequestでそのJOSNファイルにアクセスしても問題なくデータを取得、解析できます。しかし、このJSONファイルのURIを直接叩いてアクセスした場合、ブラウザはContent-Typeを元にデータ形式を認識するため、HTMLとして表示されます。そのため、以下のようなJavaScriptが埋め込まれていた場合、実行されてしまうのです。
1 | { “data”: “<script>alert(‘xss’);</script>” } |
Content-Typeにapplication/jsonを指定していれば、ほとんどのブラウザの場合実行されることはありません。また、メディアタイプを正しく指定してもまだ問題は発生する可能性はありますが、正確にデータのやり取りをする上でもContent-Typeを指定する必要性があります。
参考: