rhanda | 元銀行員Web系エンジニアの日記

実務未経験からWeb系受託開発企業に転職したひよっこエンジニアが覚えたことや日々の感情を残すブログ

『Web API The Good Parts』読みました

構成

1章 Web APIとは何か
2章 エンドポイントの設計とリクエストの形式
3章 レスポンスデータの設計
4章 HTTPの仕様を最大限利用する
5章 設計変更をしやすいWeb APIを作る
6章 堅牢なWeb APIを作る

感想

基礎的な用語から解説がなされていたので、1つずつ理解しながら読み進められてよかったです。

TwitterGitHubなど、世のサービスはどうなっているかが例に挙げられていて、面白みを感じながら読み進めることができました。
また「なぜデファクトスタンダードに寄せるのが良いのか」みたいなところから、それがどうして良いのかの根拠を言語化して示してくれていたので、自分の知識として活用できそうでした。

実際に以前プロジェクトの中で追加したAPIと同じパスが例に出てきて、その時は先方のエンジニアが提案してくださってその名前になったのですが、デファクトスタンダードになっていたのだなあと改めて思いました。
(この本もその方が薦めてくださったもの)

学んだメモ

  • Web APIは簡単にいえば「HTMLの代わりにプログラムがより処理しやすいデータ形式を返すWebページの一種」
  • APIエコノミー
    • Web APIを公開することで外部サービスとの連携を容易にして新たな価値が生まれ、サービスやビジネスが発展していくこと
  • サードパーティJavaScript
    • Facebookのいいね」など他のページに貼り付けるウィジェットなどのJavaScriptファイル
    • ブラウザを使って他のページからAPIへのアクセスが行われるため、クロスドメインでのアクセスのサポートなどを行う必要がある。
    • クライアント側のコードは誰でも読むことができてしまうため、なりすましや不正アクセスにより注意
  • スマートフォンアプリケーションにおいてサーバとクライアントを繋ぎこむWeb APIについて、モバイルアプリケーションでは一度インストールされたらアップデートされるまで古いコードが動作を続けてしまうので、APIのアップデートなどを戦略的に行う必要がある。
    • (「スマートフォンアプリの旧バージョンがこのコード使ってるから変えられない」みたいなのあったなあ💭)
  • Web APIを美しく設計するには
    • 仕様が決まっているものに関しては仕様に従う
    • そうでないものはデファクトスタンダードに従う
      • メリット:他のAPIを利用してきた開発者が利用方法を類推しやすくなったり、既存のクライアントライブラリの流用が可能になるなど

エンドポイント設計

  • 改造しやすいHackableなURIは望ましい
    • e.g. v1/items/123
    • 「このアイテムのIDが123だな」と予想できる。またこの数字を変えると別アイテムにアクセスできると予想できる
      • applicaiton/x-www-form-urlencoded以外の形式で送ることができないデメリット
  • URIで単語をつなげる必要がある場合はハイフンかアンスコか
    • ある程度好みだがハイフンが固そう
    • ドメインではハイフンが使えるがアンスコ使えないので、それに合わせる感じ
    • 実際は単語をつなぎ合わせることを避ける方が好ましい
      • popular_usersではなくusers/popularとするなどパスとして区切る方が、URIとして見やすい
  • 1スクリーン1APIコール、1セーブ1APIコール
    • 何度もAPIへのアクセスを繰り返すことは、速度の問題だけでなくデータの一部だけが表示・保存されたりするなどの問題に繋がる
    • APIのアクセス回数が増えると利用者にとって煩雑な上、HTTPのオーバーヘッドも上がってアプリケーション速度が低下する。サーバ側の負荷が上がる可能性もある
  • HATEOAS
    • APIの返すデータ中に次の行動・取得するデータのURIをリンクとして含めることで、データを見れば次にアクセスすべきエンドポイントがわかるような設計。
{
    "friends": [

        { "name": "Taro",
            "link": {
                "uri": "https://hogehoge",
                "rel": "user/detail"
      }
  ]
}
  • REST LEVEL3 API
    • HATEOASの概念を導入しているAPI
    • クライアントがURIを知る必要がないので、URIの変更がしやすくなるURIをHackableにする必要がなくなるというメリットがある
    • 人間が理解できないURIにもできる(アクセスされたくないURIを推測できないものにする)

レスポンスデータの設計

  • データフォーマットはJSONデファクトスタンダード
    • Twitterも1.1からJSONのみになった。
    • XMLよりJSONが広まった ← やりとりされるデータの多くは、軽量でシンプルJSONで表現可能だった。
  • JSONP ( JSON with padding )
    • JSONをラップするJSを付け加えたもの
  • JSONはトップレベルに配列ではなくオブジェクトを置く方が好ましい
  • 巨大な数値は文字列で返す
    • IDなど巨大な数を扱うとき、数値をそのままJSONとして返してしまうと問題が起こる可能性がある。これを回避するには、数値ではなく文字列で返す方法がある。
  • エラーレスポンス
    • 適切なステータスコードを使いながら、詳細をボディに格納すると良さそう。(ヘッダに含めるよりもクライアント側が処理しやすい)
    • Twitterはエラーが配列で返るようになっている。これは複数のエラーが同時に発生する場合に合理的といえる。

HTTPを最大限利用する

  • プロキシサーバ
    • クライアントとサーバの間に位置してやり取りを仲介する。キャッシュの情報を扱ったり
  • Content-Type
    • クライアントの多くはContent-Typeの値を使ってデータ形式をまずは判断するので、正しくデータを読み出せるよう適切なメディアタイプを指定しよう
  • 同一生成元ポリシー ( Same Origin Policy )
    • XHTTPRequstでは、異なるドメインに対してアクセスを行って、レスポンスデータを読み込むことができない。
  • CORS ( クロスオリジンリソース共有 )
    • これを利用すると、特定の生成元からのアクセスの場合のみ異なる生成元からのアクセスを許可できる。
    • クライアントからオリジンヘッダを送って、それがサーバ側で保持するアクセス許可対象の生成元一覧に含まれるかを確認する。
    • プリフライトリクエス
      • 生成元を跨いだリクエストが受け入れ可能かを事前に確認する。(HEAD, GET, POST以外のリクエストの時etc.)

設計変更しやすいWeb APIをつくる

  • APIの変更は難しい。ではどうするか→
    • 新しいものを提供する
    • 新しいものはパスでそのことがわかるようにする(v1/, v2.0/ etc.)
  • バージョン管理
    • パスの先頭に付けるのがURIに埋め込む場合には最も一般的でわかりやすそう
    • 「バージョンを日付で表す」という一般的ではないが伝統的な方法もある。
      • あまり使われていないのは長くて覚えづらいからや、古い日付をバージョンで使っていると古臭いAPIを公開しているという印象を与える恐れがあるためと考えられる。
  • 提供終了時の対応
    • 継続的な告知、Blackout Test
    • あらかじめ提供終了時の使用を盛り込む
      • 410と公開が終了した旨のメッセージを返すなど

堅牢なWeb APIを作る

  • HTTPSを使えばほとんどの攻撃は防げるが100%ではない
  • XSS
    • ユーザーの入力を受け取ってそれをページのHTMLに埋め込んで表示する際に、JSなどを実行できてしまうというもの。セッションクッキーetc.ブラウザに保持された情報にアクセスできたり、同一生成元ポリシーの制限を受けずにサーバにもアクセスできる
    • ユーザーからの入力はそれがどう使われるかに関わらずチェックしましょう
  • XSRF
    • サイトを跨いで偽造リクエストを送ることで、ユーザーが意図しない処理をサーバに実行させる攻撃
      • 勝手に投稿するetc.
      • 対策
        • サーバ側のデータが変化するようなアクセスに関しては、GETではなくPOST, PUT, DELETEメソッドを使う
          • ←IMG要素などを用いて攻撃用コードを埋め込むことができなくなる
        • XSRFトークンを使う(最も一般的な対策)
          • 正規のフォームにワンタイムトークンを埋め込みそれが無いリクエストを拒否するもの
  • JSONハイジャック
    • APIからJSONで送られる情報を悪意ある第三者が盗み取ること。SCRIPT要素に同一生成元ポリシーが適用されないために可能となる。
    • ヘッダをあれこれするなどの対策がある
  • 大量アクセスへの対策
    • 各ユーザーごとのレートリミット(最大アクセス回数/単位時間)を決める
    • サービスの性質に合わせて良い感じにできると良さそう
    • 単位時間、現在公開されているAPIを見ると1時間くらいが多いらしい
    • 制限を超えた時は429と制限内容を返すと良さそう