Skip to content

Latest commit

 

History

History
265 lines (222 loc) · 18 KB

File metadata and controls

265 lines (222 loc) · 18 KB

サーバとの通信

ここではウェブ実装で必要となってくる知識について解説する

HTTPとは

HTTPとは(Hypertext Transfer Protocol)の略でwebの閲覧によく用いられるプロトコルである。
もともとHTTPはWebブラウザとサーバ間の通信のために設計されたが、それ以外にも使用されている(WebAPIの呼び出しなど)。
HTTPはステートレスであるがセッションレスではない。
今回の解説は、このHTTPを前提に解説を行う。 本筋から外れてしまうため解説はしないが、現在主流であるのは、このHTTPの通信を暗号化したHTTPS(Hypertext Transfer Protocol Secure)と呼ばれるプロトコルである。
HTTPSについて気になる場合は、HTTPS、SSL、TLSなどの単語で調べると良い。

TIPS:ステートレストとセッションレス

ステートレスとは State(ステート:状態)と言う単語に less(レス:ない)の意味を持った接尾辞がついた単語である。
HTTPの通信におけるステートレスとは、2つ以上の通信において、それぞれに関係性がないことを指す。
例えばログインのリクエストをサーバに対して行い、成功したとする。
HTTPはログインが成功したと言う情報(state)を保持しないため、ログイン済み限定のページにアクセスしようとすると失敗する。
もちろん、実際にはこのようになっておらず、HTTPがこのようになっていないのは、HTTPがセッションレスではないからである。
通信自体は状態を持たないが、通信にセッションの情報を乗せているため、HTTPはステートレスだがセッションレスではない。

URLの構成

インターネット上のリソースにアクセスする場合、その接続先の情報をブラウザ等に与える必要がある。 ここで使用されるのがURL(Uniform Resource Locator)である。
http://example.com:8080/hoge/index.html/?name=Taro#profile
上記のようなURLがあったとき、以下のような呼び分けをする。

  • スキーム    :通信のプロトコルを指定する。http
  • ドメイン    :接続先のアドレスを指定する。example.com
  • ポート     :接続先のサーバのポート番号を指定する。通常は省略される。8080
  • パス      :ファイルが設置されている、サーバ内の場所を指定する。hoge/index.html
  • クエリパラメータ:クライアントからデータを渡すとき、URLに含めるならここに含めるname=Taro
  • アンカーリンク :主にwebページで用いられ、ページ内の特定の場所を指定する。#profile

また、スキーム、ドメイン、ポートまでをまとめてオリジンと呼ぶ。
今回の例であればhttp://example.com:8080がオリジンである。

TIPS:オーソリティ

URLのスキーム以降、ポートまでをオーソリティと呼ぶ。
あまり見かけないが、URLのドメイン名の前に認証のためのパスワードなどを含める場合があり、これもオーソリティに含まれる。
http://hoge:[email protected]:8080/hogeのようなURLであればhoge:[email protected]:8080を指す。
http://example.com:8080/hoge であれあば example.com:8080である。

HTTPメソッド

HTTPで通信を行うとき、メソッドと呼ばれるアクションを指定する。 仕様書で定義されているHTTPメソッドを以下に示す。

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD
  • CONNECT
  • OPTION
  • TRACE
  • PATCH

今回は頻繁に使用されるGETPOSTの二つについて解説する。

GETとは

サーバからデータを取得する際に使用する。URLにクエリパラメータを含めることもできる。
クエリパラメータは人の目に触れやすい場所に付与されるため、他人に見られて困る内容(個人情報等)はGETで送信してはいけない。
GETメソッドは、HTTPメソッドとしてべき等であり安全であるとされる。

ここで言う安全とはセキュリティ的な安全を指す言葉ではなく、サーバ側へ更新の要求をしないことを指す言葉である。

POSTとは

サーバのデータを更新する時に使用する。データ自体はbodyに含めて通信を行う。
ただし、暗号化を行うわけではないので注意が必要である。
個人情報などを扱う場合はHTTPSなどを利用して、通信そのものを暗号化する必要がある。
HTTPメソッドとして、べき等ではなく安全でないとされる。

なお、このbodyに含めた情報のことをペイロードと言う。

TIPS:べき等とは

HTTPメソッドでは、ある一つのリクエストを複数回処理しても、一回リクエストを処理した時と、サーバのリソース(データ)が同一の状態になることをべき等性があるという。

リクエストにおけるべき等と安全

ここではHTTPメソッドにおける定義から、GETやPOSTについてべき等性・安全性を説明した。
ただし、GETメソッドやPOSTメソッドを用いたAPIリクエストについてのべき等性・安全性は、メソッドの定義に依らず、サーバー/クライアントの実装に依存することに注意したい。

実際にやってみる

実際にGETとPOSTを使用した通信を見てみる
まず、GETの通信を見てみる

  1. 新しいタブを開いて開発者ツールを開く
    • もしそのブラウザで既にgithubにログインしている場合はシークレットブラウザを起動
  2. Networkタブを開く Networkタブの参考画像
  3. github.comにアクセス
  4. 開発者ツールに表示されている項目からgithub.comと表示されているものを探し、メソッドがGETであることを確認。
  5. ページをリロードしたり、左上のgithubのロゴマークを何度かクリックしてみてページの内容が変わらないことを確認(べき等であることを確認)
  6. github中央上部の検索窓にjiginternなど好きな文字列をいれて検索をしてみる
  7. 結果が表示されたら開発者ツールの中からsearchを探し、GETメソッドで呼び出されていること、Request URLの中にjiginternなどの自分が入力したの文字列が含まれていることを確認
  8. 同じ文字列なら何度検索をかけても同じ内容が表示されることを確認

続いてPOSTの通信を見てみる

  1. githubのログインを行う
  2. ログイン完了後、開発者ツールに表示されている項目からsessionを探し、POSTで呼び出されていること、URLにはパスワードが入っていないが、payloadタブを開くとパスワードが入っていることを確認

まとめ

  • GETで情報を送るときは、URLのパスの後ろにクエリパラメータとして付与される

  • GETで人に見られてはいけない情報は扱わない

  • GETはべき等であると定義されているので何度呼び出されても変わらない処理のみに使う

  • POSTは情報を送るとき、bodyの中にペイロードとして持つ

  • POSTはパスワードなど人に知られてはいけない情報で使う

  • ただし、POST自体は暗号化を行わない

  • POSTはべき等ではないので呼び出されるたびにサーバ側のデータが更新されるような処理に使う

  • HTTPメソッドは動作や性質が定義されているが、動作を保証するわけではない

  • サーバ側が定義通りの実装をする必要がある

HTTPの中身

サーバとの通信を行うための規格であるHTTPのリクエストの構成は主に以下の4つである。

  • メソッド(GET、POSTなど)
  • パス(ホスト以下の部分)
  • プロトコルバージョン (HTTP 1.1など)
  • リクエストヘッダ (詳細は後ほど)

また、POSTなら追加でbodyを持つ。

レスポンスの構成は主に以下の4つである。

  • プロトコルバージョン
  • ステータスコード (200など)
  • ステータスメッセージ (OKなど)
  • レスポンスヘッダ(詳細は後ほど)
  • レスポンスボディ(GETメソッドでリクエストしたときにhtmlなどがここに入る)

なお、レスポンスのボディは省略可能。(POSTの場合は不要なことがある)

リクエストヘッダとレスポンスヘッダ

ここに追加の情報を載せることで主に通信を行う。 主なリクエストのヘッダを以下に示す。

  • user-agent
  • cookie
  • host
  • accept
  • accept-language
  • accept-encoding

また、主なレスポンスヘッダを以下に示す。

  • content-type
  • Content-Encoding
  • set-cookie
  • connection
  • server

なお、ヘッダは基本的にブラウザなどが必要なものを自動的に付与するため、開発者が必要なヘッダを付与する必要はあまりない。
必要ではないだけで、任意の自作のヘッダなど好きなヘッダを付与することもできる。
ただし、既に定義されているヘッダは避ける必要があるため、慣れるまではヘッダ自作や付与はブラウザに任せるのが良い。

実際に見てみる

GETの中身を確認

  • 開発者ツールを開いたブラウザのタブを開く
  • ログインした状態の github.comを開く(リロードする)
  • Nameのカラムにgithub.comと書いてある行をクリック
  • リクエストヘッダやレスポンスヘッダ、レスポンスボディなどを確認する

POSTの中身を確認

  • 開発者ツールを開いたブラウザのタブを用意
  • githubにログイン(ログイン中の場合はサインアウトして再度サインイン)
  • NameのカラムがSessionになっているものをクリック
  • ヘッダやペイロードを確認

まとめ

  • リクエストもレスポンスも、送受信したいデータ以外にもさまざまな情報を持っている
  • ヘッダから通信の情報を得ることができる
  • ヘッダを任意に追加できるが、基本的にやらなくて良い

CORSとは

Cross-Origin Resource Sharingの略。
オリジンを跨いでデータの送受信を行える。
オリジンとは、スキーム(プロトコル)、ホスト(ドメイン)、ポート、この3つによって構成されるものである。
ブラウザにはCSRFやXSSといった攻撃を防ぐために、異なるオリジン間ではデータを送受信できないようにする同一オリジンポリシーが存在する。
しかし、HTMLやJavaScriptを取ってくるオリジンとアクセスするAPIのオリジンが異なることがあり、このような場合に不都合が出るためCORSが考案された。
CORSは安全だと認められたオリジン間のみ、跨いでデータの送受信を行うことができる。
CORSを使用するにはサーバ、クライアントでそれぞれヘッダを付与する必要がある。
なお、一定の条件を満たす場合を除いて、プリフライトリクエストを行い安全性の確認を行う。

CORSリクエスト

CORSリクエストを行う場合はリクエストヘッダに originを付与する必要がある。
このとき、レスポンスにはAccess-Control-Allow-Originのヘッダが付与され(サーバー側はこのヘッダを付与する必要がある)、このヘッダの内容とオリジンが一致するとき、レスポンスを受け取る。
なお、以下の条件をすべて満たすとプリフライトリクエスト(後述)を発生させないCORSリクエストとなる。

  • 特定のHTTPメソッドで通信を行う
    • GET
    • HEAD
    • POST
  • 特定のヘッダのみが付与されている
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
    • Range
  • Content-Typeに指定できるもの
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • XMLHttpRequest オブジェクトを使用してリクエストを行う場合は、XMLHttpRequest.upload プロパティから返されるオブジェクトにイベントリスナーが登録されていないこと。すなわち、XMLHttpRequestインスタンスを xhr とした場合、どのコードも xhr.upload.addEventListener() が呼び出してアップロードを監視するためのイベントリスナーを追加していないこと。
  • リクエストに ReadableStreamオブジェクトが使用されていないこと

実際に見てみる

  1. /transfer-protocolに移動
  2. deno run server.jsを実行
  3. 別のターミナルでdeno run client.jsを実行
  4. ブラウザでhttp://localhost:8000にアクセスして開発者ツールを開く
  5. try corsボタンを押して NetworkタブやConsoleに出力されたデータを確認する。
  6. try cors (simple request)ボタンを押して、NetworkタブやConsoleに出力されたデータを確認する。

軽く解説

サーバは3000番のポートで動作するように設定されており、クライアントは8000番で動作するようになっている。
ポート番号が異なるためCORSを用いてアクセスする必要がある。
try corsボタンは/corsを、try cors (simple request)/cors-additional-headerのエンドポイントを叩くようになっている。
/cors-additional-headerはヘッダーにAccess-Control-Allow-Origin:*を付与してレスポンスを返しているが、/corsは何も付与していない。
従ってtry corsボタンはエラーを吐くが、try cors (simple request)ボタンは正常にレスポンスを受け取ることができ、メッセージを描画することができる。

プリフライトリクエスト

前述の条件を満たせなかった場合、主にユーザのデータに影響があるリクエストの場合、プリフライトリクエストが発生する。 CORSリクエストの前に OPTIONメソッドを用いてプリフライトリクエストを送信し、そのレスポンスが安全だったときに通常のリクエストを行う。
プリフライトリクエストでは、クライアント側のヘッダには originに加えて Access-Control-Request-MethodAccess-Control-Request-Headersを付与する必要がある。
サーバ側のレスポンスヘッダには Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Max-Ageを付与する必要がある。
これらのヘッダの結果により、安全性が確保できた場合、単純リクエストと同様のヘッダで通信を行う。

実際に見てみる

  1. /transfer-protocolに移動
  2. deno run server.jsを実行
  3. 別のターミナルでdeno run client.jsを実行
  4. ブラウザでhttp://localhost:8000にアクセスして開発者ツールを開く
  5. try cors (prefright request)ボタンを押して NetworkタブやConsoleに出力されたデータを確認する。

軽く解説

try cors (prefright request)ボタンはPOSTメソッドで、bodyにJSON形式のデータを持ってリクエストを行う。
そのため、Content-Type: application/jsonのヘッダを持つ必要があるが、これはCORSの単純リクエストとしては許可されていない。
そこで、プリフライトリクエストを送り、このレスポンスに必要な情報があるか確認している。

まとめ

  • オリジンを跨ぐアクセスを許可する場合はCORSの設定を行う
  • 特定の条件を満たせない場合プリフライトリクエストという、安全性の確認用の通信が発生する
  • 外部のWebAPIを呼び出すときはCORSの設定が必要になる可能性あり

最後に

ここまでサーバとの通信に使われる技術について紹介してきた。 最後に実装していく上で意識することを中心にまとめる。

HTTPメソッドは、個人情報を扱うかデータを変更するならPOST、クライアントがデータを要求するだけ(サーバ側のデータを更新しない)ならGETを利用する。
その他にもHTTPメソッドは仕様として定義されているが、実装に慣れてきてから利用していくで問題ない。 ただし、どのHTTPメソッドを利用するにしても、サーバ側の実装は原則そのメソッドの仕様を守って実装する必要がある。
HTTP通信のヘッダは基本何もせず、ブラウザが自動で自動で付与するヘッダのみで問題ない。
CORSの設定について、サーバ実装においてはあまり気にしなくて良い。
これは、自分で実装したサーバに別のオリジンからアクセスされることがないからである。
クライアント側は外部のAPIを叩くときにCORSの設定をする必要があるが、fetchAPIを利用する場合、modeをCORSにすれば基本的に問題なく動作する。

もし、より深く理解したい場合は参考文献等を利用して自学することをお勧めする。

参考文献