「Webを支える技術」を読んだ話
この本を読んだ背景
現在とあるIT企業でエンジニアとして働いているのですが、もともと文系出身でインターン先で叩き上げでスキルを付けていたこともあり、最近プライベートでのコーディングや業務をしているときでもWebやネットワークの知識がないとこれから絶対成長できないと思ったことがきっかけです。
なのでまずは本からインプットを得て自分の中の理解を深め体系的に理解しよう計画の第一歩にしようと考えました。
これから3ヶ月間、月に1冊のペースでWeb周りの知識をつけるために、以下の本を読もうと思っています。
今回はその第一弾として「Webを支える技術」読んだので自分への復習も含めて、ここにアウトプットしたいと思います。
Webを支える技術 山本陽平(著)
HTTP, URI, HTMLなどの仕様、歴史、設計思想を理解することでより良いWebサービス設計をすることができる。その第一歩となる基礎知識をインプットすることができる本。
つまり、Webサービスを作る上で欠かせないHTTP, URI, HTMLなどについて深く知ることでより良いWebサービスを設計しましょう!という本です。
ここからWebの話に入ります。
Webが全世界に普及された理由として、「シンプル」という言葉がキーワードがあります。
これまでWebに相当するようなシステムはいくつか考案、開発されました。ですがそのどれもが高機能でやれることが多い代わりに扱うのがとても難しいものばかりでした。
Webはその点、複雑性をかなり排除し、シンプルさに重点を置いていることで誰にでも扱いやすくまた、ここまで大規模なシステムを実現することができたのです。
そもそもWebは1990年にTim Berners Leeという人により考案、実装されました。そして1993年のMosaicブラウザによりWebが広く普及されるようになります。
Webの特徴として、様々なブラウザから接続することができる点があります。それはクライアントとサーバ間のインターフェースをHTTPという通信プロトコルで固定したことで実現できています。
これだけ大きなシステムが正常に動作しているのは歴史的に見てもWebしかないでしょう。
ではなぜそれが、実現できているのでしょうか
それを知るためには、Webの設計思想を知る必要があります。
WebはRESTアーキテクチャスタイルを採用しています。
RESTはRoy Fieldingという人が学生時代に書いた博士論文から来ています。
RESTは「統一/階層化/コードオンデマンド/クライアント/キャッシュ/ステートレスサーバ」です。
- クライアント/サーバ: ユーザインターフェースと処理を分離します
- ステートレスサーバ: サーバ側でアプリケーションの状態を持ちません
- キャッシュ: クライアントをサーバの通信回数を減らします
- 統一インターフェーズ: インターフェース固定します
- 階層化システム: システムを階層に分離します
- コードオンデマンド: プログラムをクライアントにダウンロードして実行する
という特徴を持っています。
URIについて
URIとはUniform Resource Identifierの略で、「統一リソース識別子」です。
URIを使うとWeb上にある全てのリソースにアクセスすることができます。
例えば、
http://blog.example.jp/entries/1
というURIに対して
- URIスキーム: http
- ホスト名: blog.example.jp
- パス: /entries/1
と分類できます。
URIとはリソースを識別するための識別しです。
そしてURIには「Cool URIs don’t change」という言葉があります。これはURIの良し悪しを判断する際に使用される言葉で良いURIは変わらないという意味です。
Webとはハイパーメディアをリンクでつなぐ。全てのリソースは、URIによってアクセスすることができるという特徴があります。
そのためURIが変わってしまうとハイパーメディアに埋め込んだリンクでリソースにアクセスできなくなってしまいます。
そのためURIが変わりにくいことはとても大きな意味を持つのです。
変わりにくいURIにするために
URIはリソースを表現する名詞にするというものがあります。これは、動詞はHTTPメソッドで指定し、URIとHTTPメソッドの役割を名詞と動詞の関係にすることで特定のリソースに対する取得や更新を同じURIで行うことができます。
簡単にURIについてまとめると
- URIはリソースの名前である
- URIの寿命は長い
- いいURIは変わらない
があります。
HTTPについて
HTTPとは、OSI参照モデルで言うところのアプリケーション層で実現される通信プロトコルです。
仕組みは至ってシンプルで、クライアントが出したリクエストをサーバーが処理してレスポンスを返すだけです。
リクエストもレスポンスもHTTPはメッセージを残します。それをHTTPメッセージといいます。
リクエストメッセージ
GET /test HTTP1.1
Host: example.jp
これはこう解釈できます。
- HTTPメソッド: GET
- パス: /test
- 使っているHTTPバージョン: HTTP1.1
- Host: example.jp
レスポンスメッセージ
HTTP1.1 200 OK
Content-Type: application/xhtml+xml; charset=utf-8
<html xmlns="http://www.w3.org/1999/xhtml">
...
</html
- 使っているHTTPのバージョン: HTTP1.1
- ステータスコード: 200
- ステータスメッセージ: OK
- レスポンスのコンテンツタイプ: application/xhtml+xml;
- 文字エンコーディング: charset=utf-8
- レスポンスのボディ
HTTPの特徴
- ステートレス性
HTTPメソッド
- GET: リソースの取得
- POST: 子リソースの作成、リソースのデータの追加、その他の処理
- PUT: リソースの更新、リソースの作成
- DELETE: リソースの削除
- HEAD: リソースのヘッダーの取得
- OPTIONS: リソースがサポートしているメソッドの取得
- TRACE: 自分宛てにリクエストメッセージを返す試験
- CONNECT: プロキシ動作のトンネル接続への変更
これらのメソッドは、リソースの作成、取得、更新、削除をするために使用されます。これのことをCRUDと略します。
HTTPには「冪等性」と「安全」という性質が存在します。「冪等性」とは、同じリクエストをすると何回やっても同じレスポンスが返ってくることを保証するものです。「安全」は操作対象のリソース状態を変化させないこと(副作用がないこと)です。
ここでHTTPメソッドに照らし合わせて見ると、
- GET, HEAD: 冪等かつ安全
- PUT, DELETE: 冪等だが安全でない
- POST: 冪等でも安全でもない
と分類できます。ただしHTTPメソッドをただしく 運用しないと上記の冪等性と安全が保証されないこともあります。例えば
GET /resources/1/delete HTTP1.1
Host: example.jp
上記の場合、GETリクエストを呼んではいるものの、 /resources/1
を削除するGETのため、リソースの状態が変化してしまいます。これでは本来GETが持っている安全性が失われてしまいます。
ステータスコード
HTTPはリクエスト/レスポンス型の通信プロトコルで、リクエストには必ずレスポンスが返ります。そのレスポンスメッセージの中でステータスコードというものが重要な役割を果たします。
ステータスコードには先頭の数字でステータスを分類することができます。
- 1xx: 処理中
処理が継続していることを示します。クライアントはそのままリクエストを継続するか、サーバの指示に従ってプロトコルをアップデートして再送信します。
- 2xx: 成功
リクエストが成功したことを示します。
- 3xx: リダイレクト
他のリソースへのリダイレクトを示します。クライアントはこのステータスコードを受け取ったとき、レスポンスメッセージの
Location
ヘッダを見て新しいリソースへ接続します。
- 4xx: クライアントエラー
クライアントのエラーを示します。原因はクライアントのリクエストにあります。エラーを解消しない限り正常な結果が得られないので、同じリクエストをそのまま再送信することはできません。
- 5xx: サーバエラー
サーバエラーを示します。原因はサーバ側にあります。サーバ側の原因が解決すれば、同一のリクエストを再送信して正常な結果が得られる可能性があります。
そして下記がよく使われるステータスコードです。
- 200(OK): リクエスト成功
- 201(Created): リソースの作成成功
- 301(Moved Permanently): リソースの恒久的な移動
- 303(See Other): 別URIの参照
- 400(Bad Request): リクエストの間違い
- 401(Unauthorized): アクセス権限不正
- 404(Not Found): リソースの不在
- 500(Internal Server Error): サーバ内部のエラー
- 503(Service Unavailable): サービス停止
ステータスコードを正しく使用して、適切な処理をシステムにさせることがユーザが混乱しないだけでなく、システム自体が混乱しないことにもつながるのでとても大事です!
HTTPヘッダ
ヘッダはメッセージのボディに対する付加情報(メタデータ)を表現します。クライアントやサーバはヘッダを見て挙動を決定します。メディアタイプや言語エンコーディング、リソースへのアクセス権、キャッシュの有無など様々なことを設定することができます。
よく使われるHTTPヘッダ
- 日時
値に日時を持つヘッダです。条件付きGETで利用する更新日時やキャッシュの有効期限などを設定することができます。
- MIMEメディアタイプ
メッセージでやり取りをするリソースの表現の種類を指定することができます。メッセージのボディの種類を示す
Content-Type
や文字エンコーディンを示すcharset
などを設定することができます。
- 言語タグ
リソース表現の自然言語を指定するヘッダです。
コンテントネゴシエーション
上記のヘッダ達は、サーバからの一方的な決定だけでなく、クライアントを交渉をすることができます。そのことをコンテントネゴシエーションといいます。
- Accept: 処理できるメディアタイプを伝え
クライアントが自分の処理できるメディアタイプをサーバに伝える場合は、
Accept
ヘッダを利用します。クライアントがAcceptヘッダで指定したメディアタイプにサーバが対応していなかった場合は、406(Not Acceptable)エラーが返ります。
- Accept-Charset: 処理できる文字エンコーディング
- Accept-Language: 処理できる言語を伝えます。
Content-Lengthとチャンク転送
Content-Length
ヘッダを使用することでボディの長さ指定することができます。静的なファイルなど予めサイズが分かっているリソースを転送する場合はこのヘッダを利用すると簡単です。
チャンク転送とは、ボディを分割して転送することを言います。動的に画像を生成するようなWebサービスの場合、ファイルのサイズが決まるまでレスポンスを返せないので応答性が低下してしまいます。そんなときに Transfer-Encoding: chunked
を指定することでサイズが分からないボディを少しづつ転送することができます。
認証
リソースのアクセス制限を行う際に使用する認証情報をクライアントに通知することができます。HTTP1.1ではBasic認証、Digest認証が主流です。 WWW-Authenticate
ヘッダを利用できます。
Basic認証
ユーザ名とパスワードによる認証方式です。ユーザ名とパスワードを
Authorization
ヘッダに入れてリクエストごとに送信します。このBasic認証はBase64というエンコーディングを指定しているため、簡単デコードができます。つまり、ユーザ名とパスワードが平文でネットワークを流れていることを意味します。なのでBasic認証を使うときはその程度のセキュリティで問題ないかを検討する必要があります。
Digest認証
Basic認証よりもセキュアな認証方式です。あるメッセージに対してハッシュ関数を適用した結果のハッシュ値のことです。
現在では、HTTPとSSL/TLSを組み合わせたHTTPSという通信プロトコルがあります。これは通信路を暗号化してクライアントとサーバの間でやり取りするデータを保護し、盗聴を防ぐ目的で利用されます。
キャッシュ
キャッシュとはサーバから取得したリソースをローカルストレージに蓄積し、再利用する手法のことです。ローカルストレージにキャッシュされているデータそのものをキャッシュと呼ぶことがあります。クライアントが蓄積したキャッシュは、そのキャッシュの有効期限内で、再度アクセスされたときに再利用されます。 Pragma, Expires, Cache-Control
ヘッダを用いてサーバが指定します。
- Pragma: キャッシュを抑制します。
- Expires: キャッシュの有効期限を示します。
- Cache-Control: 詳細なキャッシュ方法を指定します。
持続的接続
HTTP1.1での大きな新機能です。HTTP1.0ではクライアントがTCPコネクションを確立してリクエストを送信し、サーバがそれにレスポンスを返すたびにコネクションを切断していました。ただTCPコネクション確率はコストが掛かる処理で、画像や外部CSSファイルにたくさんリンクしているWebページを表示しようとすると、もっさりした動作になっていました。それを解決するために、クライアントとサーバの間でリクエストのたびに切断していたコネクションをまとめて接続し続ける手法が開発されました。これはデフォルトで動作するので、明示的にコネクションをリクエストごとに切断したい場合は、 Connection: close
ヘッダを使用します。
以上がHTTPについてです。
最後に
今回は大枠ではありますが、Webを支える技術の中でも、Web, URI, HTTPにフォーカスして書きました。HTTPメソッドは普段の実装時から馴染みのあるものだったのですが、HTTPヘッダは見てはいたけど何をしているのかあまり理解できていなかったなと自分の穴を埋められたような気がします。HTTPのステータスコードなどは、頻繁に使われるものだけでも覚えておくことでどの場所でエラーが発生しているのか、何が原因でエラーが発生しているのかの大枠をステータスコードだけで知ることができます。これはつまりデバックのスピードが格段に上がると思っています。まだまだ表面をなでただけなので、次に読むReal World HTTPでHTTPについて深ぼって行きたいと思います。長い文章でしたが最後までお付き合いいただきありがとうございました!