「Webを支える技術」読書メモ
書籍
「Webを支える技術」
著者:山本 陽平
出版年:2010年
出版社:技術評論社
リンク:https://www.amazon.co.jp/Webを支える技術-HTTP、URI、HTML、そしてREST-WEB-PRESS-plus/dp/4774142042
概要
会社の目標のために「Webを」を読み、社内で共有しました。
以下の内容は、社内で共有する用のアウトプットとなります。
読んだ背景
Webに関するインフラ側の知見がまだ浅いと感じたためです。
バックエンドでもフロントエンドでも、キャッシュやセッションなどある程度は知っておく必要があるため、読むことにしました。
読書メモ
第1部: Web概論
第1章:Webとは何か
1.1 全ての基盤であるWeb
現代の私たちのとって、最も重要なソフトウェアはブラウザである。
ブラウザを使うことで、商品を購入したり動画を閲覧することができるためであり、すでに私たちの生活に深く根付いている。
1.2 さまざまなWebの用途
Webは具体的に、以下の3つの用途で使われている。
Webサイト
検索サイトや、Wikiなどのサービスを提供するものがある。
基本的には、クライアント側はサーバー側の構成がどのようになっているか意識しないでも使うことができる。
ユーザーインタフェースとしてのWeb
ルータやテレビなどのデバイスの設定もWebで行われることが多い。
HTMLでのヘルプの記述なども、ユーザーインタフェースでのWebの利用にあたる。
プログラム用APIとしてのWeb
XMLやJSONをの形で、プログラム用のデータの受け渡しができる。
1.3 Webを支える技術
HTTP、URI、HTML
Webをささえる最も基本的な技術は、HTTP、URI、HTMLである。
URIによって、世界中のあらゆる情報を指し示すことができる。
HTMLによって、それらの情報を表現する文書のフォーマットができ、HTTPによってそれらの情報を取得したり、発注したりできる。
ハイパーメディア
ハイパーメディアとは、テキストや画像などのさまざまなメディアを、ハイパーリンクで結びつけて構成したシステムである。
ハイパーメディア以前は、書籍や映画など、線形的に先頭から順に視聴するのに対し、ハイパーメディアでは、自分でリンクを選択して取得することができる。
分散システム
Webでは、1つの中央コンピュータがすべてを制御するのではなく、複数のコンピュータを組み合わせて処理を分散させている。
Webのようなシンプルなプロトコルによって、分散システムを実現することができる。
1.4 本書の構成
特にないので割愛します。
第2章 Webの歴史
2.1 Web以前のインターネット
インターネットの起源は、1969年に構築されたARPANETであり、それをもとに徐々に成長していった。
以前は様々なプロトコルが使われており、TCP/IP以外にバケツリレー方式のUUCPも使われていたため、メールなどは相手に送信するまでに遅延があったりした。
2.2 Web以前のハイパーメディア
Web以前のハイパーメディアは以下のようなものが存在していた。
Memex
ハイパーメディアの起源であり、電気的に接続した本やフィルムを相互にリンクし、リンクをたどって次々と表示するシステムであった。
Xanadu
「ハイパーメディア」と「ハイパーテキスト」という言葉を考案し、ハイパーテキストは文字中心の文書を相互にリンクさせた概念であり、ハイパーメディアはその考え方を拡張し、音声や動画など多様なメディアを相互にリンクさせた概念である。
Xanaduは高機能ゆえの複雑さから開発に頓挫し、失敗した。
HyperCard
カードと呼ばれる文書を単位に相互にリンクをはり、スクリプト言語HyperTalkによりプログラプを実行できるものであった。
HyperCardは成功を収め、初の実用的なハイパーメディアとなった。
Web以前のハイパーメディアの問題点
Web以前のハイパーメディアは機能が多く、複雑であったためWebほどは普及しなかった。
Webは単方向リンクしかサポートしていないなど機能は少なかったが、必要最低限のリンク機能だけを備えていたために普及した。
2.3 Web以前の分散システム
集中システムと分散システム
初期のコンピュータは一つのコンピュータで集中して処理する形態だった。
1970年代以降、ダウンサイジングが進んで全体としての性能を向上させる手法が登場してくる。
RPC
分散システムを実現する技術の一つとして、RPCがある。
RPCを使うと、リモートのサーバで実行しているプログラムをクライアント側から呼び出すことができる。
CORBA・DCOM
RPCは関数を呼び出す仕組みであったが、現代的なプログラミング言語はほとんどすべてがオブジェクト指向機能を備えている。
そのため、オブジェクト自体をリモート側に配置する分散オブジェクトが考案され、CORBAとDCOMが開発された。
Web以前の分散システムの問題点
RPC自体は現在も使われているものの、以下の問題があるため、利用はイントラネットまででとどまっている。
・性能劣化の問題
→ネットワーク越しの関数呼び出しは、何回も関数を呼び出すことにより同一プロセス内での呼び出しよりも何倍も時間がかかってしまう。
・データ型変換の問題
→プログラミングごとにデータ型が異なるため、複数の言語が混在する状況では型を変換する必要がある。
・インタフェースバージョンアップ時の互換性の問題
→機能追加の伴ってサーバのインタフェースを更新した場合、下位互換性を保てない。
・負荷分散の問題
→RPCはサーバ上にクライアントのアプリケーション状態を保存するため、サーバ間でその状態を共有する必要があり、負荷分散が難しい。
2.4 Webの誕生
ハイパーメディアや分散化の影響を受け、Webが誕生した。
ハイパーメディアとしてのWeb
Webはインターネットを使ったハイパーメディアとして設計されたため、不特定多数の情報をリンクさせ合うことができ、システムを大規模化しやすいという利点がある。
また、単方向リンクだけが使われており、ユーザーにとってわかりやすく実装が簡単なためここまで普及したと考えられる。
分散システムとしてのWeb
RPCは閉じたネットワーク環境での利用では優れているものの、オープンなネットワーク環境での利用には向いていない。
Webは、インタフェースをHTTPというシンプルなプロトコルで固定したことにより、オープンなネットワーク環境でも問題なく利用できる。
2.5 Webの標準化
Webの仕様策定
Webはあまりにも急速に普及したため、初期は標準化が間に合わなかった。
標準化をするために、W3C(World Wide Web Consortium)が設立された。
Internet ExplorerとNetscape Navigatorが独自拡張を繰り返した結果、HTMLやCSSのレンダリング結果が大きく異なり、現在でも残る「ブラウザ対応」という問題になっている。
さまざまなハイパーメディアフォーマットの誕生
Webページの新着情報をサーバで配信し、専用のプログラムでチェックする用途で、RSSが提案された。
しかし、複数のバージョンが乱立したため、Atomが標準化された。
HTMLや、AtomはXMLを元にしたマークアップ言語のため、冗長な表記が多く、データの受け渡しをするためのフォーマットとしてJSONがデファクトスタンダードとなった。
2.6 Web APIをめぐる議論
SOAP対REST
SOAPベースの技術はベンダーが推進していたのに対し、RESTは一人の研究者が推進しており、SOAP優勢で進められていた。
議論されている最中に、AmazonがWeb APIを作成した際、SOAP形式とREST形式で作成し、REST形式の方がシンプルでつかいやすいため、利用比率は20:80ほどとなっていた。
最終的には、RESTの方が手軽であったことと、GoogleとAmazonなどの企業はREST形式でWeb APIを提供し始めたため、RESTに軍配が上がった。
SOAPが使われなかった要因としては、各ベンダーごとにSOAPの利用方針に違いが出てしまったことと、RESTよりも仕様が複雑であったためと考えられる。
第3章 REST Webのアーキテクチャスタイル
3.1 アーキテクチャスタイルの重要性
RESTはWebのアーキテクチャスタイルである。
アーキテクチャスタイルは、システムのアーキテクチャを決定する際の羅針盤となる。
3.2 アーキテクチャスタイルとしてのREST
RESTはWeb全体のアーキテクチャスタイルでもあり、個別のWebサービスやWeb APIのアーキテクチャスタイルでもある。
RESTの約束を守ることで、全体が統一したアーキテクチャスタイルを守ることができる。
3.3 リソース
リソースとは、「Web上に存在する名前を持ったありとあらゆる情報」である。
リソースの名前は、あるリソースと他のリソースを区別して指し示すためのものである。
リソースの名前としてのURI
リソースの名前とはURIのことであり、リソースとは以下の特徴を持つ。
- リソースとは、Web上の情報である。
- 世界中の無数のリソースは、それぞれURIで一意の名前を持つ
- URIを用いることで、プログラムはリソースが表現する情報にアクセスできる。
リソースのアドレス可能性
URIがない場合、例えばファイルの場所を教える場合、FTPサーバログイン情報やディレクトリ構造などを教える必要がある。
URIがあることで、簡単にリソースを指し示すことができ、その性質をアドレス可能性という。
同じリソースはURIを複数持つことができるが、いくつもつけると正式なURIがわかりにくくなってしまう。
3.4 スタイルを組み合わせてRESTを構成する
Webは、HTTPでクライアント/サーバのアーキテクチャスタイルを採用している。
クライアント/サーバの利点は、処理をクライアントとサーバに分離することができるところであり、それによってクライアントをマルチプラットフォームにすることができる。
ステートレスサーバ
ステートレスとは、サーバがクライアントのアプリケーション状態を持たないことであり、サーバー側の実装を簡略化できる利点がある。
しかし、Cookieなどのセッション管理など、ステートレスでないWebサービスもある通り、状況によってはあえてステートレスサーバの利点を捨てることもある。
キャッシュ
キャッシュの利点として、サーバとクライアントの通信を減らすことができる点があるが、キャッシュが古い場合、情報の信頼性が下がる可能性もある。
統一インタフェース
URIでの操作を統一インタフェースにより制限することで、全体のアーキテクチャをシンプルにすることができる。
例えば、HTTP1.1では、GETやPOSTなどのメソッドが8個のみであり、その制約によってアーキテクチャがシンプルになっている。
階層化システム
サーバやクライアントの間にロードバランサを設置して負荷分散をしたり、プロキシを設置してアクセス制限をすることができる。
どちらであっても、同じインタフェースで接続できるため、接続先がサーバからプロキシに変わったことを意識する必要はない。
コードオンデマンド
コードオンデマンドは、プログラムコードをサーバからダウンロードし、クライアント側でそれを実行するアーキテクチャスタイルであり、JavaScriptなどがこれに該当する。
欠点としては、ネットワーク通信のプロトコルの可視性が低下することである。
REST=ULCODC&SS
RESTは以下のアーキテクチャスタイルを組み合わせたものである。
- クライアント/サーバ:ユーザインタフェースと処理を分離する
- ステートレスサーバ:サーバ側でアプリケーション状態を持たない
- キャッシュ:クライアントとサーバの通信回数と量を減らす
- 統一インタフェース:インタフェースを固定する
- 階層化システム:システムを階層に分離する
- コードオンデマンド:プログラムをクライアントにダウンロードして実行する
3.5 RESTの2つの側面
RESTとハイパーメディア
Webを普段使う時、ブックマークにサイトを追加したり、ブックマークの一覧を見たりする。
RESTでは、そのような特徴を「アプリケーション状態エンジンとしてのハイパーメディア」という。
RESTと分散システム
他の分散オブジェクトでは、関数やメソッド単位でサーバ側の処理を呼び出すが、RESTではリンクを辿ることでアプリケーションを実現するため、粒度が大きい状態でデータをやり取りすることができる。
また、HTTPメソッドのような統一インタフェースを使うことで、互換性の問題なく使うことができる。
第2部: URI
第4章: URIの仕様
4.1 URIの重要性
URIとは、統一リソース識別子のことで、Web上のすべてのリソースを一意に示すことができる。
4.2 URIの構文
例えば、以下のURIの構成は次のようになっている。
- URIスキーム:http
- ホスト名:blog.example.jp
- パス:/entries/1
URIはURIスキームで始まり、URIスキームの後ろは「://」で区切られる。
URIスキームの次はホスト名が出現し、ホスト名はDNSで名前が解決できるドメイン名かIPアドレスで、インターネット上で必ず一意になる。
ホスト名の次にはパスが続き、パスはホストの中でリソースを一意に指し示す。
以下のような場合は、次のようになる。
- URIスキーム:http
- ユーザ情報:yohei:pass
- ホスト名:blog.example.jp
- ポート番号:8000
- パス:/search
- クエリパラメータ:q=test&debug=true
- URIフラグメント:#n10
URIスキームの次にユーザ名とパスワードが入っており、ユーザ名とパスワードは「:」で区切る。
ユーザ情報の次は区切り文字の@が入っており、ホスト情報が続く。
ホスト情報はホスト名とポート番号に分かれており、「:」で区切られる。
パスの後ろには区切り文字である「?」がつき、名前=値の形式のクエリが続く。
クエリパラメータが複数ある場合は2つ目以降のクエリパラメータが&となる。
最後のURIフラグメントはその前までの文字列で表現するURIのリソース内部のさらに細かい部分を特定するときに利用する。
4.3 絶対URIと相対URI
OSのファイルシステムでは、ルートから記述したパスを絶対パスと呼び、現在のディレクトリからのパスを相対パスという。
URIも同様に絶対パス・相対パスが存在する。
ベースURI
相対パスはそのままではクライアントが解釈できないため、相対パスのベースとなるベースURIを設定する。
その方法として、以下の2つの方法がある。
リソースのURIをベースURIとする方法では、あるリソースを取得したときにそのリソースのURIをベースURIとして相対パスを解決する。
しかし、ベースURIをクライアント側で保持しておく必要がある。
ベースURIを明示的に指定する方法では、HTMLやXMLにbase要素を追加することで、明示的にベースを指定できる。
4.4 URIと文字
URI仕様では、次の文字がURIのパスに使用できると定められている。
- アルファベット:A-Za-z
- 数字:0-9
- 記号:-.~:@!$&'()
日本語の文字を直接入力することはできないため、エンコーディングして入れる必要がある。
%エンコーディング
URI使用が許可している文字以外をURIに入れるには、%エンコーディングでその文字をエンコードする。
%という文字列は、エンコーディング用の文字列となるため直接入力できず、URIに%を入力する場合は%25という表記になる。
%エンコーディングは、UTF-8を使うことを前提とした方式であるため、文字コードによってエンコードする文字列が異なる。
この問題は、Webページのエンコーティング方式に合わせる形で対応できるが、基本的にWebサイトの多くはUTF-8を採用している。
4.5 URIの長さ制限
長さ制限はないが、Internet Explorerでは、実装上2038バイトまでという制限がある。
4.6 さまざまなスキーム
URIスキームの種類はHTTP以外にもあるが、既存のHTTPスキームを使えば済むため、基本的に従来にないプロトコルを発明したとき以外は考慮しなくてもよい。
4.7 URIの実装で気をつけること
URIでは、相対URIの解決と%エンコーディングの扱いに気をつける必要がある。
解決の手間がかかるため、なるべく絶対URIを使った方がよく、%エンコーディングではUTF-8でエンコーディングするのが良い。
第5章 URIの設計
5.1 クールなURIは変わらない
URIが頻繁に変わってしまうと、いままでアクセスできていたリソースが急に見れなくなったりする。
そのため、URIはなるべく変わらない方が良い。
5.2 URIを変わりにくくするためには
プログラミング言語に依存した拡張子などを含めない
たとえば、「 http://example.jp/cgi-bin/login.pl 」 のようなURIでは、CGIを使わなかったり、Rubyなどで書きたいときに使いにくくなってしまう。
メソッド名やセッションIDを含めない
メソッド名やセッションIDを含めると、ログインするたびにセッションIDが変わるため、URIがシステムにログインし直すと変更になってしまう。
URIはリソースを表現する名詞にする
実装に依存したURIが作られてしまう場合、HTTPメソッドではなくURIによって「取得したいか」や「更新したいか」が変わってしまう。
例えば「show」や「update」などがパスに入っていた場合、URIによってやりたいことが変わってしまうため、URIが変更される要因になりうる。
URIの設計指針
- URIにプログラミング言語依存の拡張子を利用しない
- URIに実装依存のパス名を利用しない
- URIにプログラミング言語のメソッド名を利用しない
- URIにセッションIDを含めない
- URIはそのリソースを表現する名詞である
5.3 URIのユーザビリティ
シンプルなURIであれば、開発者でない普通の人も覚えやすくなり、使いやすくなる
5.4 URIを変更したいとき
URIを変更したい時は、リダイレクトを設定すると良い。
リダイレクトを設定した場合、ユーザが古いURIにアクセスした場合に新しいURIに遷移させることができる。
5.5 URI設計のテクニック
拡張子で表現を指定することで、自動的に適した表現を返すことができる。
例えば、以下のようなURIであれば、日本語版なのか英語版なのかを明示的に指定できる。
日本語版:http://example.jp/2010/05/01/press.jp
英語版:http://example.jp/2010/05/01/press.en
日付であれば、パスの階層で表現できるが、地図の場合は難しいため、マトリクスURIを使う。
マトリクスURIでは、階層の代わりに「;」を使って文字を区切る。
5.6 URIの不透明性
URIがわかりやすい場合、5.5のようなURIであればフランス語版は「〜/press.fr」であると推測できてしまう。
しかし、そこにリソースがあるとは限らないため、URIは不透明な方が良い。
5.7 URIを強く意識する
URIは次の点でとても重要である。
- URIはリソースの名前である
- URIは寿命が長い
- URIはブラウザがアドレス欄に表示する
第3部: HTTP
第6部: HTTPの基本
6.1 HTTPの重要性
HTTPは名前はハイパーテキストの転送用であるが、実際は静止画や音声・動画なども転送できる。
6.2 TCP/IPとは何か
HTTPはTCP/IPをベースにしており、TCPとIPはネットワークの重要なプロトコルである。
TCPはトランスポート層に属し、IPはインターネット層に属する。
ネットワーク層では、特定のIPアドレスにデータをパケットとして送り出す。
トランスポート層では、データの抜け漏れをチェックし、ポート番号によってどのアプリケーションに送り届けるかをチェックする。
HTTPはアプリケーション層に属し、ポート番号はデフォルトで80を使う。
6.3 HTTPのバージョン
HTTP0.9
HTTP0.9はGETメソッドだけであり、現在ではほとんど使われていない。
HTTP1.0
ヘッダの導入やGET以外のメソッドの追加などHTTP1.1につながる足掛かりとなった。
HTTP1.1
コンテントネゴシエーションや複雑なキャッシュコントロールができ、現在でも使われている。
→本書が2010年出版となっているためですが、現在の最新はHTTP2であり、複数のリクエストを同時に処理できるなど、HTTP1.1よりも高速になっている。
6.4 クライアントとサーバ
HTTPはクライアントがサーバにリクエストを出してレスポンスを受け取る流れとなっている。
6.5 リクエストとレスポンス
HTTPが同期的なプロトコルであるため、クライアントは、リクエストを送ってからレスポンスが返ってくるまで待機する仕様となっている。
クライアントで行われること
- リクエストメッセージの構築
- リクエストメッセージの送信
- (レスポンスが返るまで待機)
- レスポンスメッセージの受信
- レスポンスメッセージの解析
- クライアントの目的を達成するために必要な処理
サーバで行われること
- (リクエストの待機)
- リクエストメッセージの受信
- リクエストメッセージの解析
- 適切なアプリケーションプログラムへの処理の委譲
- アプリケーションプログラムから結果を取得
- レスポンスメッセージの構築
- レスポンスメッセージの送信
以下の流れとなる。
ク1→ク2→サ2→サ3→サ4→サ5→サ6→サ7→ク4→ク5→ク6
6.6 HTTPメッセージ
リクエストメッセージ
リクエストメッセージの1行目はリクエストラインとよばれ、メソッド・リクエスト・プロトコルバージョンからなる。
GET /test HTTP/1.1
Host: example.jp
絶対URIを用いた場合、リクエストURIは以下のようになる。
GET http://example.jp/test HTTP/1.1
レスポンスメッセージ
レスポンスメッセージの1行目はステータスラインとよばれ、プロトコルバージョン・ステータスコード・テキストフレーズからなる。
2行目以降はヘッダであり、リクエストメッセージと同様である。
HTTP/1.1 200 OK
Content-Type: application/xhtml+xml; charset=utf-8
<html xmlns="http://www.w3.org/1999/xhtml">
...
</html>
6.7 HTTPのステートレス性
HTTPはステートレスであり、日常会話はステートフルなやり取りである。
ステートレスではやりとりが増えるため冗長ではあるが、ステートフルな場合クライアントのアプリケーション状態を覚える必要があり、クライアントの数の分だけ覚えておく必要がある。
ステートレスにすることにより、クライアントの状態を覚えておく必要がないため、管理しやすくなる。
ステートレスの欠点
ステートレスの欠点として、送信するデータ量が多くなるなどパフォーマンス低下の欠点がある。
また、通信エラーが発生した場合、1つ前の処理を実行したのかどうか分からず、重複して実行してしまう可能性がある。
6.8 シンプルなプロトコルであることの強み
HTTPはシンプルなプロトコルであるため、WebサービスとWeb APIを同じプロトコルで実現できる。
第7章
7.1 8つしかないメソッド
HTTPのメソッドは、以下の8つのみであり、TRACEとCONNECTはほぼ使われていない。
- GET:リソースの取得
- POST:子リソースの作成、データの追加
- PUT:リソースの更新・作成
- DELETE:リソースの削除
- HEAD:リソースのヘッダの取得
- OPTIONS:リソースがサポートしているメソッドの取得
- TRACE:自分宛にリクエストメッセージを返す
- CONNECT:プロキシ動作のトンネル接続への変更
7.2 HTTPメソッドとCRUD
特にGET・POST・PUT・DELETEはCRUD操作に対応している。
7.3 GET
GETでは、リソースの取得を行う。
7.4 POST
POSTでは、リソースの作成・追加を行う。
主に使われるのはリソースの作成だが、ログの追加を行うような場合、POSTメソッドでログの追加が行われる。
GETではURIに含めていたキーワードは、POSTではリクエストボディに含めて実現できる。
7.5 PUT
PUTでは、リソースの更新ができる。
また、更新対象のリソースがもしなかった場合、作成することもできる。
POSTとPUTの使い分けとしては、POSTはクライアントにURIの決定権がないため、基本的にリソースの作成はPOSTで行うのが良い。
7.6 DELETE
DELETEはリソースを削除できる。
7.7 HEAD
リソースのヘッダの取得
HEADはGETに似ているが、ボディが含まれないため、ネットワークの帯域を節約しながらリソースの大きさを調べたり、リソースの更新日時を取得したりする。
7.8 OPTIONS
OPTIONSは、そのリソースがサポートしているメソッドの一覧を返す。
7.9 POSTでPUT/DELETEを代用する方法
HTMLのフォームでは、GETとPOSTしか使えない。
そのため、何らかの方法でPUTやDELETEを伝える必要がある。
_methodパラメータ
フォームの隠しパラメータに、「_method」を用意し、そのValueにPUTやDELETEを追加する。
X-HTTP-Method-Override
POSTの内容がXMLなどの場合には、_methodパラメータが使えないため、X-HTTP-Method-Overrideヘッダを使うことでPUTとして扱うことができる。
7.10 条件付きリクエスト
HTTPメソッドと更新日時などのヘッダを機見合わせることで、メソッドを実行するかどうかをサーバが選択でき、そのようなリクエストを条件付きリクエストという。
7.11 べき等性と安全性
べき等性とは、何回同じ処理を行なっても結果が同じことを意味する。
通信エラーが起きた際、PUTとDELETEはべき等であり、GETは副作用がないため、べき等かつ安全といえる。
POSTだけはべき等でもなく安全でもないため、POSTを複数回送ることに慎重になる必要がある。
7.12 メソッドの誤用
GETメソッドだが、そのメソッドで削除や更新処理をする場合は誤用にあたるので注意が必要となる。
PUTやDELETEで実現できる機能をPOSTで行うことも避けた方がよく、その場合はGET・PUT・DELETEのべき等性が利用できなくなってしまう。
PUTで、「値段を+50円する」など送った場合、元々が100円だった場合に1回目の送信で「100+50」となり、2回目で「100+50+50」となってしまう。
べき等にするため、完全な金額を送り、更新する形にするのが良い。
DELETEでエイリアスを使って削除した場合、例えば/latestならば最新のリソースが常に削除されてしまい、べき等性が保たれなくなるため避けた方が良い。
7.13 Web成功の理由はHTTPメソッドにあり
HTTPメソッドのべき等性やシンプルさなどによって、Webが成功したといえる。
第8章 ステータスコード
8.1 ステータスコードの重要性
ステータスコードは、Web APIやWebサービスを設計するにあたって重要である。
8.2 ステータスラインのおさらい
200はクライアントのリクエストが正常終了したことを示す。
8.3 ステータスコードの分類と意味
ステータスコードは以下のように分類される。
- 1xx: 処理中
- 2xx: 成功
- 3xx: リダイレクト
- 4xx: クライアントエラー
- 5xx: サーバエラー
8.4 よく使われるステータスコード
200 OK
リクエストが成功した際に使われる。
201 Created
リソースが新たに作成された際に、POSTやPUTのレスポンスとして返される。
301 Moved Permanently
リクエストで指定したリソースが新しいURIに移動したことを示す。
303 See Other
リクエストに対する処理結果が別のURIで取得できることが示す。
400 Bad Request
リクエストの構文やパラメータが間違っていることを示す。
401 Unauthorized
適切な認証情報を与えずにリクエストされたことを示す。
404 Not Found
指定されたリソースが見つからないことを示す。
500 Internal Server Error
サーバ側に何らかの異常が生じており、正しいレスポンスが返せないことを示す。
503 Service Unavailable
サービスのメンテナンスなどで一時的にアクセスできないことを示す。
8.5 ステータスコードとエラー処理
4xx系と5xx系はどちらもエラーを表現するが、ボディにどのようなエラーメッセージを入れるかは規定されていない。
そのため、クライアントが解釈できる形式でエラーメッセージを返してあげるとが親切である。
例えば、Acceptヘッダに応じて、AtomならAtom形式で、HTMLならHTML形式で返すとよい。
8.6 ステータスコードの誤用
ステータスコードは正しく使う方が良い。
例えば、ファイルが見つからないエラーを200で返してしまうと、クライアントが正式なリソースだと勘違いしてインデックス処理が行われてしまうことがある。
8.7 ステータスコードを意識して設計する
エラーが起きたときに、どのステータスコードなどはとても重要な検討事項である。
第9章 HTTPヘッダ
9.1 HTTPヘッダの重要性
ヘッダはボディの付加的な情報を返し、認証やキャッシュなどのHTTPの機能はヘッダで実現する。
9.2 HTTPヘッダの生い立ち
HTTPヘッダの詳細は、メールの仕様も関わっており、Content-TypeヘッダやDateヘッダは電子メールでも使われている。
9.3 日時
日時はDateヘッダやExpiresヘッダが相当する。
9.4 MIMEメディアタイプ
Content-Typeヘッダは、そのメッセージのボディがどのような内容・種類なのかをメディアタイプで示す。
charsetパラメータは、UTF-8など文字エンコーディングを指定する。
9.5 言語タグ
リソース表現の自然言語を指定するヘッダも存在し、Content-Languageヘッダである。
方式は「ja-JP」のようになっており、左側に言語タグが入り、右側に地域コードが入る。
9.6 コンテントネゴシエーション
メディアタイプなどは、サーバが一方的に指定するだけではなく、クライアントと交渉して決めることもできる。
Accept - 処理できるメディアタイプを伝える
クライアントがAcceptヘッダでメディアタイプを指定する。
Acceptヘッダで指定したメディアタイプにサーバが対応していなかった場合は、406エラーを返す。
Accept-Charset - 処理できる文字エンコーディングを伝える
クライアントが処理できる文字エンコーディング方式を伝えることができる。
Accept-Language - 処理できる言語を伝える
クライアントが処理できる言語を伝えることができる。
9.7 Content-Lengthとチャンク転送
Content-Length - ボディの長さを指定する
メッセージがボディを持っている場合、そのサイズを10進数のバイトで表現する。
チャンク転送
動的に画像を生成するようなWebサービスの場合、ファイルサイズが決まるのが遅い。
「Transfer-Encoding: chunked」を指定することで、最終的なサイズがわからないボディを少しずつ転送できるようになる。
9.8 認証
HTTPの認証方式には、Basic認証とDigest認証がある。
Basic認証
ユーザ名とパスワードによる認証方式で、Authorizationヘッダに入れてユーザ名とパスワードはBase64方式でエンコードして送ることができる。
ただ、Base64方式は簡単にデコードできるため、HTTPSで使うことを検討する必要がある。
Digest認証
Digest認証はBasic認証よりもセキュアな方式となる。
WWW-Authenticateヘッダにて、以下のパラメータを使う。
- nonce: リクエストごとに変化する文字列
- qop: authかauth-initを指定し、authの場合はメソッドとURIからダイジェストを作成するが、auth-initの場合はメソッドとURIとメッセージボディからダイジェストを作成する。
- opaque: クライアントには推測できない文字列で、同じURI空間へのリクエストでは共通してクライアントからサーバに送る。
ダイジェストの生成と送信は以下の流れで行う。
- ユーザ名、realm、パスワードを「:」で連結し、MD5ハッシュ値を求める。
- メソッドとURIのパスを「:」で連結し、MD5ハッシュ値を求める。
- 1.の値、サーバから得たnonce、クライアントがnonceを送った回数、クライアントが生成したnonce、qopの値、2.の値を「:」で連結し、MD5ハッシュ値を求める。
クライアントはResponseフィールドに生成したダイジェストを入れてリクエストする。
Digest認証の欠点としては、nonceがなければクライアントでダイジェストを生成できず、一旦401レスポンスを得る必要があるため、あまり普及していない。
WSSE認証
WSSE認証は、HTTP1.1標準街の認証方式である。
401レスポンスのヘッダのprofileを見てnonceと日時を連結した文字列にSHA-1ハッシュ値を求め、Base64エンコードしてX-WSSE拡張ヘッダに情報を入れてリクエストする。
9.9 キャッシュ
HTTPの重要な機能にキャッシュがある。
キャッシュ用ヘッダ
Pragmaはキャッシュを抑制し、no-cacheを値に入っていた場合、クライアントはリソースをキャッシュしてはならない。
Expiresはキャッシュの有効期限を示し、キャッシュが有効期限内かどうかによってサーバに再度アクセスするのかどうか決定する。
Cache-Controlは、PragmaとExpiresよりも複雑な指定ができる。
そのため、次の方針で使い分けると良い。
- キャッシュをさせない場合は、PragmaとCache-Controlでno-cacheを同時に指定する。
- キャッシュの有効期限が明確に決まっている場合は、Expiresを指定する。
- キャッシュの有効期限を相対的に指定したい場合は、Cache-Controlのmax-ageで早退時間を指定する。
条件付きGET
条件付きGETにより、キャッシュを再利用できる可能性がある。
If-Modified-Sinceでは、リソースの更新日時を条件にして、レスポンスの値を変更する。
キャッシュを使う場合は、ボディを減らしてレスポンスを返すため、ネットワーク帯域を節約できる。
If-None-Matchでは、リソースのETagを条件にし、「指定された値にマッチしなければ」を条件にしてレスポンスを変更する。
サーバがEtagヘッダを使っているときは、In-None-Matchを使う方が正確に更新の有無が判断できる。
9.10 持続的接続
HTTP1.1では、Keep-Aliveヘッダにより、接続し続けることができるようになった。
コネクションを切断した場合、Connectionヘッダにcloseという値を設定することで切断できる。
9.11 そのほかのHTTPヘッダ
Content-Dispositionは、サーバがクライアントに対してそのリソースのファイル名を提示するために利用するヘッダである。
Slugは、ファイル名のヒントを指定することができる。
9.12 HTTPヘッダを活用するために
HTTPヘッダを学ぶためには、多様な知識が必要となる。
第4部: ハイパーメディアフォーマット
第10章: HTML
10.1 HTMLとはなにか
HTMLは、マークアップ言語であり、タグで文書の構造を表現する。
10.2 メディアタイプ
HTMLのメディアタイプは、「text/html」と「application/xhtml+xml」の2種類がある。
10.3 拡張子
HTMLは、「.html」と「.htm」を使い、現在は「.html」が一般的である。
10.4 XMLの基礎知識
XML文書は木構造として表現できる。
XMLは要素を開始タグ・内容・終了タグで表し、開始タグは「<要素名>」、終了タグは「</要素名>」と記述する。
要素は複数の属性を持つことができ、「属性名="属性値"」で記述する。
文字によっては、XMLの文書構造を表す特別な文字となるため、文字として参照する場合はエスケープして表現する必要がある。
XML文書中には、コメントを書くことができる。
XML宣言では、XMLのバージョンや文字エンコーティング方式を指定し、エンコーディング方式は一般にはUTF-8を利用するのが良い。
複数のXMLフォーマットを使う場合、名前が衝突することがあるため、名前空間を指定することで区別できる。
10.5 HTMLの構成要素
ヘッダ
ヘッダに入る要素はメタデータとなる。
ボディ
ボディに入る要素は、ブロックレベル要素とインライン要素がある。
ブロックレベル要素は、見出しや段落などを扱う。
インライン要素は、ブロックレベルの中に入る要素で、強調や画像埋め込みなどを表現する。
共通の属性
HTMLのすべての要素は、id属性とclass属性を持つことができる。
id属性は文書内で一意なIDであり、Class属性はCSSのスタイルの指定などを行う。
10.6 リンク
<a>要素
他のWebページにリンクするために、<a>要素を使うことができる。
<link>要素
<a>要素はHTMLのブロック要素の中でのリンクであるのに対し、<link>要素はWebページ同士の関係を指定するために使う。
オブジェクトの埋め込み
<img>要素で画像を埋め込むことができ、<object>要素で動画を埋め込むことができる。
フォーム
キーワード検索やユーザからの入力によってURIを生成することができる。
<a>や<link>はGETしか発行できなかったのに対し、フォームではPOSTを発行することができる。
10.7 リンク関係
rel属性
<a>と<link>はrel属性を持つことができ、リンク元のリソースとリンク先のリソースの関係を記述できる。
microformats
HTMLのリンク関係は、もともとマニュアルなどの文書を想定していたが、現在はさまざまなリソースをHTMLで表現している。
HTMLのリンク関係の拡張をmicroformatsで行う。
10.8 ハイパーメディアフォーマットとしてのHTML
HTTPやURI、ハイパーメディアによるリンクを組み合わせて初めてWebが成り立つ。
第11章 microformats
11.1 シンプルなセマンティックWeb
microformatsは、従来のセマンティックWebよりも軽量のセマンティックWebを実現できる。
11.2 セマンティクス(意味論)とは
Webにおける意味論は、リソースが持つ意味を確定させるための理論である。
人間が読んで理解するWebページの意味を、プログラムからも処理できるように形式的に意味を記述するための技術がセマンティックWebである。
11.3 RDFとmicroformats
RDFは、主語・述語・目的語の3つの組みを使って、Web上のリソースにメタデータを与えて、プログラムがリソースの意味を処理できるようにする。
RDFは、汎用的な反面記述が冗長になり、統一的な記述がしづらい欠点があった。
microformatsでは、元のHTMLの要素に最低限の情報を追加しているだけで済むなど、RDFの欠点を解消できる。
11.4 microformatsの標準化
microformatsは、HTML文書そのものにメタデータを埋め込むものであり、一部は標準化されている。
11.5 microformatsの分類
microformatsには、elemental microformatsとcompound microformatsがある。
elemental microformats
rel-licenseでは、a要素につけることで、リック先の参照先が参照元のページのライセンス情報であることを示す。
rel-nofollowは、SEOに影響を与えないようにすることができる。
compound microformats
hCalendarは、カレンダー情報やイベント情報を記述するためのmicroformatsである。
hAtomは、更新日時やパーマリンクなどのメタデータを埋め込むことができる。
11.6 microformatsとRDFa
microformatsの問題点として、たまたま同じ値のclass属性やrel属性を持ったWebページがあった場合、プログラムが誤判定したりすることがある。
RDFaでは、microformatsが持つ名前の衝突問題を、XMLの名前空間で解決できる。
第12章 Atom
12.1 Atomとは何か
XMLフォーマットであり、RSSとは違い、ブログだけでなく検索エンジンなど様々なWeb APIとして利用できる。
12.2 Atomのリソースモデル
Atomの論理モデルは、メンバリソースとコレクションリソースに分かれる。
メンバリソース
Atomの最小のリソース単位であり、ブログであれば、一つ一つの記事がメンバリソースになる。
コレクションリソース
コレクションリソースは、複数のメンバリソースを含む。
メディアタイプは「application/atom+xml」であり、拡張子は「.atom」である。
12.3 エントリ
エントリはAtomの最小単位である。
エントリは以下のメタデータを持つ。
- id
- タイトルと概要
- 著者と貢献者
- 公開日時と更新日時
- カテゴリ
- リンク
エントリの内容
組み込みで定義されている内容は、「プレーンテキスト」・「エスケープ済みHTML」、「XHTML」がある。
12.4 フィード
メンバリソースを複数持つコレクションリソースの表現がフィードである。
feed要素はエントリと同じメタデータを持つことができる。
フィードは、エントリと共通のメタデータ以外に、次の4つのメタデータを持つ。
- サブタイトル
- 生成プログラム
- アイコン
- ロゴ
12.5 Atomの拡張
Atom以外の拡張要素は、外部マークアップと呼ばれる。
Atom Threading Extensions
スレッド機能を実現するには、Atomの標準要素だけでは表現しきれず、Atom Threading Extensionsの利用が必要となる。
Atom License Extension
Atom License Extensionは、フィードやエントリのライセンス情報を表現することを目的にしている。
Feed Paging and Archiving
検索結果を分割し、複数のフィードに分けたい場合、複数のフィードに分かれていることを表現するのがFeed Paging and Archivingである。
OpenSearch
OpenSearchは検索エンジンのWeb APIのベースとなる仕様であり、さまざまな検索サービスが活用されている。
第13章 Atom Publishing Protocol
13.1 Atom Publishing Protocolとは何か
Atomはデータフォーマットの規定であり、AtomPubはAtomを利用したリソース編集プロトコルの規定である。
AtomPubはRESTのスタイルに基づいたプロトコル仕様であり、基本的なリソースモデルとリンク機構を提供しているため、実装工数の削減ができる。
13.2 AtomPubのリソースモデル
AtomPubでは、Atomが規定しているリソースモデルをベースにエントリを操作する。
13.3 ブログサービスを例に
ブログサイトのトップページはAtomのコレクションリソースにあたり、コレクションリソースにある記事の最新の一覧のHTML表現である。
13.4 メンバリソースの操作
エントリ単位の操作では、各エントリ固有のURIをもつため、それぞれのURIに以下のHTTPメソッドを適用すればCRUD操作を実現できる。
- GET: エントリの取得
- POST: エントリの作成
- PUT: エントリの更新
- DELETE: エントリの削除
メディアリソースの操作
メディアリソースの作成はPOSTで行い、更新はPUTで行うことができる。
13.5 サービス文書
ブログサービスの中でも、複数のユーザのブログを持っていたり、メディアリソース専用のコレクションリソースを持っていたりする。
AtomPubのサービス文書では、コレクションリソースのメタデータを複数まとめて記述することができる。
13.6 AtomPubに向いているWeb API
ブログサービスのAPIや、検索機能を持つデータベースのAPIは、AtomPubに向いていると考えられる。
第14章 JSON
14.1 JSONとは何か
データのフォーマットであり、Javascriptの記法でデータを記述することができる。
14.2 メディアタイプ
JSONのメディアタイプは「application/json」である。
14.3 拡張子
拡張子は、「.json」である。
14.4 データ型
データ型は以下の通りである。
- オブジェクト
- 配列
- 文字列
- 数値
- ブーリアン
- null
JSONには日時型がないため、文字列に変換して表現する必要がある。
リンクについても、文字列で表現し、絶対URIにしておく方が無難である。
14.5 JSONPによるクロスドメイン通信
Ajaxで用いるXMLHttpRequestモジュールは、単一のドメインとしか通信ができない。
JSONPは、script属性を活用して、クロスドメイン通信を実現する。
14.6 ハイパーメディアフォーマットとしてのJSON
JSONはJavaScriptをベースにしたデータフォーマットであると同時に、ハイパーメディアフォーマットである。
JSONを表現するには、ほかのリソースとの関係を考慮してリンクをしっかり入れた設計をすることが重要である。
第5部 Webサービスの設計
第15章 読み取り専用のWebサービスの設計
15.1 リソース設計とは何か
リソース設計とは、クライアントとサーバ間のインタフェース設計、WebサービスやWeb APIの外部設計である。
リソース設計では、Webサービスで提供するのもWeb APIで提供するのも同じ技術を使っているため、分けて考えないことが重要である。
15.2 リソース指向アーキテクチャのアプローチ
リソース設計の指針として存在するのは、リソース指向アーキテクチャであり、次のステップからなる。
- Webサービスで提供するデータを特定する
- データをリソースに分ける
- リソースにURIで名前をつける
- クライアントに提供するリソースの表現を設計する
- リンクとフォームを利用してリソース同士を結びつける
- イベントの標準的なコースを検討する
- エラーについて検討する
15.3 郵便番号検索サービスの設計
例として、以下の要件のサービスを仮定する。
- 日本の郵便番号情報を提供する
- 郵便番号の前方一致で、郵便番号情報を検索できる
- 住所および住所の読みで、郵便番号情報を全文検索できる
- データはすべて読み取り専用である
15.4 Webサービスで提供するデータを特定する
リソース設計の最初の工程として、サービスで提供するデータを理解し特定する作業が求められる。
サービスで提供するデータには、以下の3つのデータがあることがわかった。
- 7桁の郵便番号
- その郵便番号が表現する住所
- 住所のカタカナ読み
15.5 データをリソースにかける
データをリソースに分割する工程であり、このステップは大変難しい工程である。
このサービスで提供するリソースをまとめると、以下の4種類である。
- 郵便番号リソース
- 検索結果リソース
- 地域リソース
- トップレベルリソース
15.6 リソースにURIで名前をつける
郵便番号リソースは、郵便番号で一意になるため、URIに郵便番号を使うのがよい。
郵便番号は「-」のありなしの2パターンがあるため、プログラムで扱いやすい「-」なしに決め、ハイフンありの場合はリダイレクトさせるようにする。
検索結果リソースは、検索キーワードの入力を必要とし、キーワードはクエリパラメータで受け取るのが良い。
漢字でキーワードを受け取る場合は、UTF-8でエンコーディングする。
地域リソースは、地域が「都道府県・市区町村・町域」の階層を持っているため、パスで階層を表現するのが良い。
トップレベルリソースは、Webサービスのスタート地点であるため、ルートとなるURIを与えるのが良い。
15.7 クライアントに提供するリソースの表現を設計する
表現の方法として、以下のものがある。
- XML表現: XHTMLやAtom
- 軽量表現: JSONやYAML、CSV
- マルチメディア表現: 画像や映像
表現はそれぞれの状況に応じて使い分ける必要があり、郵便番号検索サービスであるため、XHTMLとJSONがよい。
著者名などが不要のためAtomよりもXHTMLがよく、JavaScriptがメインのクライアントになる可能性が高いため、JSONがよい。
リソースの表現では、URIでも指定することができ、XHTMLであれば「.html」、JSONであれば「.json」とできる。
15.8 リンクとフォームを利用してリソース同士を結び付ける
検索結果リソースでは、郵便番号はそれぞれ単体のリソースであり、URIを持つため、各郵便番号リソースへのリンクがあると良い。
また、一度に表示できる結果数に制限を設け、複数ページに分けるとよい。
地域リソースも検索結果と同様にリンクを設けるのが良い。
郵便番号リソースは、今回のサービスの最終目的のリソースとなるため、リンクは必要ないように見えるが、リンクを設定しておくことでクライアントがそのURIを使って別のアプリケーションに遷移することができる。
トップレベルリソースには、サービスが提供するリソースへのリンクを用意する。
15.9 イベントの標準的なコースを検討する
サービスにおける標準的なコースは以下の3つである。
郵便番号を検索するコース
- フォームに郵便番号を入力
- 検索結果リソースを取得
- 目的の郵便番号リソースを取得
住所から郵便番号を検索するコース
- フォームに住所を入力
- 検索結果リソースを取得
- 目的の郵便番号リソースを取得
地域リソースの階層をたどりながら郵便番号を選択するコース
- 都道府県リソースを選択し市区町村一覧を表示
- 市区町村リソースを選択し町域地位欄を表示
- 町域リソースを選択し郵便番号一覧を表示
- 目的の郵便番号リソースを取得
15.10 エラーについて検討する
今回のサービスでは、以下のようなエラーが想定される。
- 存在しないURIを指定した
→404を返す。 - 必須パラメータを指定していない
→400を返す。 - サポートしないメソッドを使用した
→405を返す。
15.11 リソース設計のスキル
リソース設計のスキルは、URI設計方法や表現選択の指針、リンクの設計などを身につけることで身につく。
第16章 書き込み可能なWebサービスの設計
16.1 書き込み可能なWebサービスの難しさ
書き込み可能なサービスの場合、複数ユーザの同時操作やバックアップなど考慮する点が多い。
16.2 書き込み可能な郵便番号サービスの設計
例として、以下の書き込み機能を追加する。
- 郵便番号リソースの作成
- 郵便番号リソースの更新
- 郵便番号リソースの削除
- バッチ処理
- トランザクション
- 排他制御
16.3 リソースの作成
ファクトリリソースをあらかじめ準備しておくことで、新しいリソースをPOSTやPUTで作成できる。
16.4 リソースの更新
リソース全体を送信する更新方法を、バルクアップデートという。
リソースの一部分だけを送信する更新方法をパーシャルアップデートという。
更新できないプロパティを更新しようとした場合、400を返して更新できないことをクライアントに伝えるか、作成日時など自動的に値を更新するプロパティの場合は無視して200を返すのが良い。
16.5 リソースの削除
リソースの削除は、単純に削除すれば良いが、例えば「東京都」を削除した場合には「東京都文京区」も削除するなど、付随するものの削除も検討する必要がある。
16.6 バッチ処理
大量の郵便番号の作成や更新は、サーバへの接続回数が多くなる可能性があるため、一気に処理できるバッチ処理を作るのが良い。
バッチが途中でエラーが起きた場合、一部の処理だけ成功した状態になる懸念がある。
その回避として、トランザクションを利用するか、207を返して複数の結果を表示する方法がある。
16.7 トランザクション
トランザクションを使うことで、全て成功した場合にのみ値を変更し、一つでも失敗した場合には全て元の状態に戻すことができる。
16.8 排他制御
複数ユーザが同じリソースを制御することを防ぐために、排他制御をかける必要がある。
排他制御には、悲観的ロックと楽観的ロックがある。
悲観的ロックをHTTPで実現する場合は、WebDAVのLOCK/UNLOCKメソッドを使う方法がある。
ロック中に何か操作が行われた場合、423 Lockedを返す。
楽観的ロックは、条件付きPUTを行うことで実現でき、DELETEにおいても条件付きDELETEを利用できる。
条件付きPUTや条件付きDELETEを発行した際にリソースが変更されていた場合は、412 Precondition Failedを返す。
16.9 設計のバランス
サービスによって品質など考慮する点が違うため、バランスを考えて設計することが必要である。
特に、以下の点が重要である。
- なるべくシンプルに保つ
- 困ったらリソースに戻って考える
- 本当に必要ならPOSTで何でもできる
第17章 リソースの設計
17.1 リソース指向アーキテクチャのアプローチの落とし穴
リソース指向アーキテクチャのアプローチの落とし穴として、「Webサービスで提供するデータの特定」と「データをリソースに分ける」方法がわからない点がある。
その対策として以下の成果物をもとに、リソースの設計を行う方法がある。
- 関係モデルのER図
- オブジェクト指向モデルのクラス図
- 情報アーキテクチャ
17.2 関係モデルからの導出
ER図を作成し、スキーマの中心のテーブル1行1行をリソースとすることが定石である。
中心となるテーブルが決まったら、DBの正規化にとらわれずにリソースが持つデータを検討する。
検索のような機能がある場合、検索をモデル化するのではなく、検索結果をリソースとすることで、リソースを導出できる。
関係モデルが苦手とする構造に階層構造があり、階層構造については、別のドキュメントなどを見ながら反映させる。
トップレベルリソースには、今回であれば検索フォームを置くのがよい。
リンクの設計は、ER図の関連が利用できる。
17.3 オブジェクト指向モデルからの導出
郵便番号データのクラス図を作成し、主要データである郵便番号クラスからリソースを導出する。
リソースはクラスで表現している処理そのものではなく、処理結果であることに注意する。
階層については、クラス同士の関係が1対nの場合は、階層関係があるといえる。
トップレベルリソースに関しては、直接表現するクラスがないため、別途意識して導出する必要がある。
リンクの設計は、クラスの参照先がそのままリンクとなる。
17.4 情報アーキテクチャからの導出
情報アーキテクチャとは、情報を整理し、ユーザにとってわかりやすく伝える手法である。
今回の要件であれば、日本郵便の検索サイトを見ることで、どのようにページが分かれているのか調べていくことでできる。
トップページでは、以下の検索方法が用意されている。
- 全国地図からの検索
- 住所での検索
- 郵便番号での検索
各サイトには、パンくずリストがあり、上の階層に戻れるようになっている。
情報アーキテクチャにて、リソース指向アーキテクチャの足りない点を補うことができる。
17.5 リソース設計で最も重要なこと
既存の3つの手法を使うことも重要だが、WebサイトとWeb APIに分けて考えないことが重要である。
Discussion