今さらですが、「Web API: The Good Parts」を読みました。
せっかくなので自分なりにまとめてみます。
1. URLについて
- 短く入力しやすいURL
×:http://sample.com/search-api/service/api/search
○:http://sample.com/api/search
- 人間が読んで理解できるURL
×:http://sample.com/api/u
○:http://sample.com/api/user
略すのもだめ products -> prod
- 大文字小文字が混在していないURL
×:http://sample.com/api/getUserInfo
○:http://sample.com/api/user
基本はすべて小文字
- 改造しやすい(Hackable)なURL
×:http://sample.com/api/item/a/12345
×:http://sample.com/api/item/b/67890
○:http://sample.com/api/item/12345
○:http://sample.com/api/item/67890
- サーバ側のアーキテクチャが反映されていないURL
×:http://sample.com/cgi-bin/get_user.php?user=100
○:http://sample.com/api/user?id=100
- ルールが統一されたURL
userはidを指定するのに、itemはクエリを使うなどの不統一はだめ
2. HTTPメソッドについて
-
GET
リソースの取得 -
POST
リソースの新規登録 -
PUT
既存リソースの更新 -
DELETE
リソースの削除 -
PATCH
リソースの一部変更 -
HEAD
リソースのメタ情報の取得※ 基本的には、GET/POST/PUT/DELETEの4つをサポートする
GET,POSTしか使えない場合
リクエストヘッダX-HTTP-Method-Overrideを使うPOST /X/XX HTTP/1.1 Host XXX.com X-HTTP-Method-Override: DELETE
3. ネーミング選定指針について
- 複数形の名詞を利用する
複数だとわかりにくい単語は単数形にしてもよい
- 極力動詞は使用しない
- 利用する単語に気を付ける
迷ったらProgrammableWebというサイトなどで探す
- スペースやエンコーディングを必要とする文字を使わない
- 単語をつなげる必要がある場合はハイフンを利用する
URIのドメイン名のルールと合っているため
4. エンドポイント設計について
- ひとつの画面を表示するためにコールするのが1つのAPIで済むようにする
- なんらかのデータをサーバに保存する場合にも1回のコールで済むようにAPIを用意する
5. レスポンスデータの設計について
- データフォーマットはJSONをデフォルトとし、必要がある場合のみXMLに対応する
- 様々なユースケースを考慮し、一度に返すデータはなるべく詳細にする
例)ユーザ情報を返す
ID、名前、写真、住所など
- ただし、データが大きすぎる場合、クライアント側で選択できるようにする
例えば、パラメータにsmall, medium, largeをつけると取得する項目を変えるようにする
- 不要な階層化はせず、なるべくフラットにする
-
もちろん階層化したほうが良い場合もある
例えば、メッセージの送信者と受信者があり、その下にユーザデータをつける場合などsender_id:12345, sender_name:aaaaa, receiver_id:67890, receiver_name:bbbbb ↓ sender: { id:12345, name:aaaaa }, receiver:{ id:67890, name:bbbbb }
- JSONのトップレベルを配列にしない
JSONインジェクションという脆弱性に対するリスクが大きくなる
※ JSONインジェクション
→ ScriptタグでJSONを読み込む手法。トップレベルが配列だと、JavaScript構文エラーにならずに、データが取得できてしまう。
- 日付のフォーマット
RFC3339を推奨
→ インターネット上で用いる標準形式として決められたもののため
例)2015-10-12T11:30:22+09:00
- 巨大な数字は文字列にして返す
18桁などの整数は、JavaScriptが64bit浮動小数として扱うため誤差がでる
- エラーの表現
- 適切なステータスコードを返すこと
エラーメッセージを返していてもステータスコードが200だとクライアント側に負担をかけることになる - エラーの際にHTMLが返らないようにする
- メンテナンスなどでサービスを止める場合
- ステータスコードは503を返す
- HTTPヘッダのRetry-Afterを使って、メンテナンスが終わる時刻を返す
- 適切なステータスコードを返すこと
6. HTTPの仕様について
- ステータスコード
- 100番台
情報 - 200番台
成功 - 300番台
リダイレクト - 400番台
クライアントサイドに起因するエラー - 500番台
サーバサイドに起因するエラー
- 100番台
- データ更新時のベストプラクティス
- PUTやPATCHは200(OK)とともに操作したデータを返却
- DELETEは204(No Contents)を返す
- クライアントエラーについて
- 401(Unauthorized)は認証エラー
- 403(Forbidden)は認可エラー
認証とは、「アクセスしてきたのが誰であるのかを識別すること」
認可とは、「特定のユーザに対してある操作の権限を許可すること」
→ つまり、401は「あなたが誰だかわからないよ」、403は「あなたが誰だかはわかったけど、この操作はあなたには許可されていないよ」
- サーバエラーについて
500(Internal Server Error)はログをきちんと監視して、管理者に通知が行くように設定しておくこと
- クロスドメイン問題について
CORSの利用
- 独自HTTPヘッダの定義について
- 「X-」という接頭辞をつける
- その後はサービス・アプリ名や組織名、そしてハイフンを挟んでIDを付ける
例)X-abc-original-id
7. バージョンについて
- APIのバージョン
URIにバージョン情報を含める方法が最もよく利用されている
例)http://api.abc.co.jp/v2/users/
- モバイルアプリのアップデートについて
アプリ起動時に、現在のバージョンとサーバ側で配信するサポートバージョンを比較し、利用者にアップデートを案内する仕組みを仕込んでおく
8. セキュリティについて
- セッションハイジャック対策
- HTTPSにする
- XSS(クロスサイトスクリプティング)対策
-
以下のレスポンスヘッダを使用する(JSONインジェクション対策にも有効)
X-Content-Type-Options: nosniff
-
JSON文字列のエスケープを行う(SCRIPTタグを無効にする)
-
- XSRF(クロスサイトリクエストフォージェリ)対策
- サーバのデータ更新を行うようなアクセスに関しては、GETメソッドを利用せずPOST, PUT, DELETEを用いること
- 正規のフォームにワンタイムトークン(少なくともセッションごとにユニークなトークン)を埋め込んでおき、そのトークンのないアクセスは拒否する
- JSONインジェクション対策
-
メディアタイプをクライアントに返す
Content-Type: application/json
-
JSONデータのトップレベルを配列にしない。必ずオブジェクトにする
-
9. 大量アクセス対策
- ユーザごとにアクセス制限する
- 何を使ってユーザを識別するか
- リミット値をいくつにするか
- どういう単位でリミット値を設定するか
- リミットのリセットをどういうタイミングで行うか
- たとえば1分間に60回をアクセスの上限とした場合、1分間の間に61回アクセスがあった場合はエラーを返し、また1分が経過したらアクセスができるようになる、といった具合
- 一般的には1時間が多い
- 制限値を超えた場合の対応
- 429 Too Many Requests を返すべき
- エラーの詳細をレスポンスに含めるべき
- Retry-Afterヘッダを使って次のリクエストまでの待ち時間を指定してもよい
- レスポンスにレートリミットを含める場合
以下はヘッダに格納する場合のデファクトスタンダード- X-RateLimit-Limit
単位時間あたりのアクセス上限 - X-RateLimit-Remaining
アクセスできる残り回数 - X-RateLimit-Reset
アクセス数がリセットされるタイミング
- X-RateLimit-Limit