📖

WebAPIについての学習 まとめ

2024/03/24に公開

概要

この記事はAPIリクエストとレスポンスの書き方を主に記載している。
APIを作成する際に気にするポイントをおさえておくことで美しいAPIができるようになる。

APIを美しく設計することの重要性

  1. APIを利用するのは自分ではないことが多い
  2. APIは変化していく
  3. WebAPIもセキュリティの問題を考慮する必要がある

公開したサービスが公開当時と2、3年後も全く同じということはあまりない。
そのインターフェイスであるAPIも変化していく。
こうしたAPIの変更を利用者に影響なく設計をすることが大事。
また、APIは関係のない第三者が多く利用することも考えられるため、
使い勝手がよくセキュリティが高いAPIを作成することが重要。

APIの書き方

・使用が決まっているものに関しては仕様に従う
・使用が存在していないものに関してはデファクトスタンダードに従う

ベースの考え方は、他の人のAPI設計やデファクトスタンダードを真似をする。
APIが完成するまでに多くの人の目を通して最適化されていることから、
そうした設計の仕様やデファクトスタンダードから設計していくことは理にかなっている。
ただ、「他のAPIに合わせました」という理由ではなく、なぜその仕様となっているのかを
理解した上で設計できるようにすること。

APIエンドポイント設計とリクエスト形式

  • エンドポイントとは
    APIにアクセスするためのURIのこと

APIエンドポイント設計について

使用者がデータベースのリレーショナルを意識するようなAPIや
ただSQLを内包したAPIではよくない。
APIはもっと高次元の機能を表すものであることが重要。

良いURI設計とは?

覚えやすく、どんな機能を持つURIなのかがひと目でわかる

具体的に言うと?

・短く入力しやすいURI
・人間が呼んで理解できるURI
・大文字小文字が混在していないURI
・改造しやすい(Hackableな)URI
・サーバー側のアーキテクチャが反映されていないURI
・ルールが統一されたURI

さらに注意点は?

・複数形の名刺を利用する
・利用する単語に気を付ける
・スペースやエンコードを必要とする文字を使わない
・単語をつなげる必要がある場合はハイフンを利用する

クエリパラメーターとパスの使い分け

クエリパラメータをパスの中に入れることも可能である。その使い分け方法。

・一意なリソースを表すのに必要かどうか
・省略可能かどうか

HTTPメソッドについて

URIは操作する対象を表し、HTTPメソッドは何をするかを表す

  • GETメソッド
    Getメソッドに対してサーバー側の情報を変更する処理を書くのはご法度。

  • POSTメソッド
    情報を更新するものとは少しニュアンスが異なる。
    新規登録に対して使用することは正しいが、既存の情報を修正や削除する際には使わない。

  • PUTメソッド
    POSTとの違いは更新したいリソースのURIそのものを指定し、その内容を書き換える。
    また、送信するリクエストデータでもともとのリソースを完全に上書きするというもので、一部書き換えるものは他のメソッドを利用する。

  • DELETEメソッド
    URIで指定したリソースそのものを削除

  • PATCHメソッド
    一部を変更するということを明示したメソッド。
    PATCHを使えば、巨大データを送信するより、更新対象の一部データを送信するほうが効率的。

APIレスポンス設計

データフォーマットの指定方法

よく利用されているのはクエリパラメータを利用する方法で、拡張子はめったにない。

  • クエリパラメータを使う
      http://api.sample.com/v1/users?format=xml
    
  • 拡張子を使う
      http://api.sample.com/v1/users.json
    
  • リクエストヘッダでメディアタイプを指定する方法
    Acceptで指定することで、このデータ形式で受け取りたいということをサーバーに伝える
      GET /v1/users
      Host: api.sample.com
      Accept: application/json
    

レスポンス内容

APIのユースケースを考えた構成にする

ただデータを返すだけでは意味がない。アプリケーションインターフェースのため、
アプリケーションの特性を踏まえたうえで、利用者が使いやすい構成にすることが大事

例)NG
  {
    "users": [
      "A0000001",
      "A0000002",
      "A0000003"
    ]
  }
例)OK 
  {
    "users": [
      {
        "id": "A0000001",
        "name": "A太郎",
        "gender":"male",
        "address": "",
        "mailaddress": ""
      },
      {
        "id": "A0000002",
        "name": "A次郎",
        "gender":"male",
        "address": "",
        "mailaddress": ""
      },
      {
        "id": "A0000003",
        "name": "A三郎",
        "gender":"male",
        "address": "",
        "mailaddress": ""
      }
    ]
  }

データをフラットにするべきかは状況次第

GoogleのJsonStyleGuide
「なるべくふらっとのしたほうがよいけど、階層構造持ったほうがわかりやすい場合もあるよね」

階層構造を持ったほうがわかりやすい場合もあれば、
階層構造がなくてもコードを利用するうえではあまり違いがなかったり、
むしろデータサイズが大きくなってしまうことがある。
なるべくフラットにするべきだけど、階層化したほうがよいとこは階層化する。

トップレベルはオブジェクトで包む

トップレベルで配列とオブジェクトのどちらでも、JSON形式としては問題ない。

オブジェクトにする理由は?

・レスポンスデータが何を示しているものかがわかりやすくなる
・レスポンスデータをオブジェクトに統一することができる
・セキュリティ上のリスクをさげることができる

データを不要なエンベロープで包まない

エンベロープ:すべてのAPIが同じデータ構造を返すために実際のデータをくるむための構造
ヘッダー情報は、HTTPのレスポンスヘッダを使って書くことができ、冗長な表現のためやるべきではない。

{
  "header": 
    {
      "status": "success",
      "errorCode": 0
    },
  "response": 
    {
      ...データ...
    }
}

各データのフォーマット

  • 各データの名前

・多くのAPIで同じ意味に利用されている一般的な単語を用いる
・なるべく少ない単語数で表現する
・複数の単語を連結する場合、その連結方法はAPI全体を通して統一する
・変な省略形は極力利用しない
・単数形/複数形に気を付ける

  • 日付のフォーマット

・RFC3339を使うのがよい

 2015-10-12T11:30:22+09:00

エラーのフォーマット

  • 適切なステータスコードを使う
ステータスコード 意味
100番台 情報
200番台 成功
300番台 リダイレクト
400番台 クライアントサイドに起因するエラー
500番台 サーバーサイドに起因するエラー

400番台は、クライアントのアクセスの仕方、パラメータ誤り、権限等のクライアント側のアクセスの方法がおかしいため発生したエラー。
500番台は、リクエストは正しかったものの、サーバー側で正しく処理ができなかったエラー。
プログラムのエラーやアクセス型、メンテナンスによる処理が継続できない場合など。

エラーの詳細情報

エラー情報は基本的にレスポンスボディにデータを入れる

エラー情報として何を入れるべき?
エラーの詳細コード、詳細情報のメッセージやリンク等
 このうちの詳細コードはAPI提供側で独自に定義して、ドキュメントに落とし込む。
 (コードの番号は、わかりやすく管理しやすいものにすること)

{
    "errors": [
      {
        "message": "Bad Authentication data",
        "code": 215
      }
    ]
}

APIのセキュリティ

  • どんなセキュリティ問題があるのか?

    ・サーバとクライアント間での情報の不正入手
    ・サーバの脆弱性による情報の不正入手や改ざん
    ・ブラウザからのアクセスを想定しているAPIにおける問題(XSS,XSRF)

APIで最低限やっておくべき対策とは?

HTTPSによるHTTP通信の暗号化
HTTPSによって暗号化されるのはURIパス、クエリ文字列、ヘッダとボディ
下記は注意すること
・SSLサーバ証明書は不正なものではないこと

Content-Typeを指定
リクエスト内に悪意のあるコードを含むリンクを生成して、Content-Typeがtext/htmlの場合
コードがHTMLと認識されて、JavaScriptが実行されてしまう。
そのため、JSONが必ずJSONであるとブラウザに判断してもらうため、Content-Typeに
application/jsonを指定して返す。
下記は注意すること
ブラウザによっては、データの内容を推測してデータ形式を推定するContent Snifferingと
いうものがある。そのため、それを防ぐために下記のレスポンスヘッダを用いること。
X-Contnet-Type-Options: nosniff

XSRFトークンや特別なリクエストヘッダを使う
XSRF(クロスサイトリクエストフォージェリ)とは、サイトをまたいで偽造したリクエストを
送り付けることにより、ユーザーが意図していない処理をサーバに実行させてしまうこと。
XSRFトークンを使うことで、正規のフォームではそのトークンやヘッダがあるため、
サーバー側でチェックをすることが可能となる。

認証を受けた悪意のあるユーザが利用する場合の対策

パラメータ改ざん
本来アクセスできないはずの情報はサーバー側でチェックし、アクセスを禁止させることが大事。
また、ユーザIDなど連番になっている可能性があるIDを変更して大量リクエストを制限するレートリミットなどを利用したり、パラメーター改ざんでマイナスの値を指定をされないようにする等考慮が必要

リクエストの再送信
「敵に勝利した」というリクエスト情報を再度送信することでもう一度同じ処理ができたとします。そうなると、報酬が2倍得られるような不正利用が可能になってしまいます。
パラメータ改ざんと違いパラメータが適切かどうかで判断できない。
同じアクセスが何度も行われた際の影響を鑑みて、エラーにするなどのチェックを行うことが必要。

参考文献

この記事は以下の文献を参考にして執筆。

Discussion