あまブログ

ドキドキ......ドキドキ2択クイ〜〜〜〜〜〜〜ズ!!

Webを支える技術【まとめ】

Webを支える技術

3章 REST

RESTの概要

  • REST
    • Webのアーキテクチャスタイル
  • アーキテクチャスタイル
    • 複数のアーキテクチャに共通する性質、様式、作法あるいは流儀を指す言葉
    • システムのアーキテクチャを決定する際の羅針盤となるもの
    • その他のアーキテクチャスタイルとしてMVC、パイプ&フィルタ、イベントシステムなどがある

Webのアーキテクチャスタイルとアーキテクチャと実装

抽象化レベル Webでの例
アーキテクチャスタイル REST
アーキテクチャ ブラウザ、サーバ、プロキシ、HTTP、URI、HTML
実装 Apache、Firefox、Internet Explorer

※上に行くほど抽象度が高い

リソース

  • リソース
    • Web上に存在する情報
    • リソースの名前 = URI
    • URIを用いることで、プログラムはリソースが表現する情報にアクセスできる

RESTの構成

RESTは以下の6つのアーキテクチャスタイルを組み合わせて構築した複合アーキテクチャスタイル

  1. クライアント/サーバ
  2. ステートレスサーバ
  3. キャッシュ
  4. 統一インタフェース
  5. 階層化システム
  6. コードオンデマンド

※RESTに基づいたアーキテクチャを構築する場合でも、これら6つのうち、いくつかを除外してもかまわない(ステートフルだがその他はRESTの制約に従っているアーキテクチャなど)

1. クライアント/サーバ

  • クライアント/サーバ
    • HTTPでクライアントとサーバが通信するアーキテクチャスタイル
    • クライアントはサーバにリクエストを送り、サーバはそれに対してレスポンスを返す
  • クライアント/サーバの利点
    • 単一のコンピュータ上ですべてを処理するのではなく、クライアントとサーバに分離して処理ができる
      • クライアントをマルチプラットフォームにできる(PC、スマホ、ゲーム機)
      • ユーザインタフェースはクライアントが担当するため、サーバはデータストレージとしての機能だけを提供すればよい
      • 複数のサーバを組み合わせて冗長化することで、可用性を上げられる

2. ステートレスサーバ

  • ステートレス
    • クライアントの状態をサーバで管理しないこと
    • ⇄ステートフル(代表例:Cookieを使ったセッション管理)
  • ステートレスサーバの利点
    • サーバ側の実装を簡略化できる
      • クライアントからのリクエストに応えたあとすぐにサーバの計算機リソースを解放できる

3. キャッシュ

  • キャッシュ
    • リソースの鮮度に基づいて、一度取得したリソースをクライアント側で使い回す方式
  • キャッシュの利点
    • サーバとクライアントの間の通信を減らすことでネットワーク帯域の利用や処理時間を縮小し、より効率的に処理できる
  • キャッシュの欠点
    • 古いキャッシュを利用してしまい情報の信頼性が下がる可能性もある

4. 統一インタフェース

  • 統一インタフェース
    • URIで指し示したリソースに対する操作を、統一した限定的なインタフェースで行うアーキテクチャスタイル
    • HTTP1.1ではGETやPOSTなどの9つのメソッドだけに限定されている
  • 統一インタフェースの利点
    • インタフェースの柔軟性に制限を加えることで全体のアーキテクチャがシンプルになる
    • インタフェースを統一することでクライアントとサーバの実装の独立性が向上する
    • システム全体を階層化しやすくなる(階層化システム)

5. 階層化システム

  • 階層化システム
    • システムをいくつかの階層に分離するアーキテクチャスタイル
  • 階層化システムの利点
    • サーバとクライアントの間にロードバランサを設置して負荷分散したり、プロキシを設置してアクセスを制限できる
      • クライアントからするとサーバもプロキシも同じインタフェースで接続できるので、接続先がサーバからプロキシに変わったことを意識する必要がない
      • これはサーバやプロキシなどの各コンポーネント間のインタフェースをHTTPで統一しているから実現できる

6. コードオンデマンド

  • コードオンデマンド
    • プログラムコードをサーバからダウンロードし、クライアント側でそれを実行するアーキテクチャスタイル
    • JavaScriptやFlash、Javaアプレットなどがこれに該当する
  • コードオンデマンドの利点
    • クライアントをあとから拡張できる
      • クライアントプログラムにあらかじめ用意した機能だけではなく、新しい機能を追加していける
  • コードオンデマンドの欠点
    • ネットワーク通信におけるプロトコルの可視性の低下
      • HTTPというアプリケーションプロトコルに従って通信している間は、通信の意味やアクセスするリソースが明白なのに対し、コードオンデマンドでプログラムをダウンロードし、クライアント側で実行してしまうと、アプリケーションプロトコルの可視性は低下する

4章 URIの仕様

  • URI(Uniform Resource Identifier)
    • リソースを統一的に識別するID
    • URIを使うとWeb上に存在するすべてのリソースを一意に示すことができる
    • URIの長さに関して、仕様上の制限はないが、Internet Explorerは2,038バイトまでという実装上の制限がある

URIの構文

# 一般的なURI
http://blog.example.jp/entries/1
# 複雑なURI
http://user:pass@blog.example.jp:8000/search?q=test&debug=true#n10
  • URIスキーム:http
  • ユーザ情報:user:pass
  • ホスト名:blog.example.jp
  • ポート番号:8000
  • パス:/search
  • クエリパラメータ:q=test&debug=true
    • q=testdebug=trueがクエリ。1つ以上のクエリの集合をクエリパラメータ(クエリ文字列)と呼ぶ
  • URIフラグメント:#n10

絶対URIと相対URI

# 絶対URI
http://example.jp/foo/bar

# 相対URI
foo/bar # 相対URIはURIスキームやホスト名を省いてパスだけで表現する
  • ベースURI
    • 相対URIの起点となるURI
    • 相対URIを絶対URIに変換するために必要(この変換を「相対URIを解決する」と言う)
    • 上記の例におけるベースURIはhttp://example.jp/
  • ベースURIを明示的に指定する方法
    • HTMLの場合
      • head要素の中にbase要素を入れる(<base href="http://example.jp/">)
    • XMLの場合
      • xml:base属性を利用する(<foo xml:base="http://example.jp/foo">)

※クライアントで相対URIを解決するには面倒な処理が必要になるので、WebサービスやWeb APIを実装する場合はなるべく絶対URIを使う

↓相対URIから絶対URIへの変換(ベースURIはhttp://example.jp/foo/bar/)

相対URI 絶対URI
hoge http://example.jp/foo/bar/hoge
./hoge http://example.jp/foo/bar/hoge
../hoge http://example.jp/foo/hoge
/hoge/fuga http://example.jp/hoge/fuga
?q=hoge http://example.jp/foo/bar?q=hoge
#hoge http://example.jp/foo/bar#hoge

/から始まる相対URIは、ホスト名からのパスとして解釈する(4行目:/hoge/fuga)

URIと文字

  • URIで使用できる文字(ASCII文字)
    • アルファベット:A-Za-z
    • 数字:0-9
    • 記号:.~:@!$&'()
  • %エンコーディング
    • 日本語などの、URI仕様が許可している文字以外の文字をURIに入れるときに使うエンコード方式
    • UTF-8の文字を構成するバイトそれぞれを%xx(xxは16進数)で記述して、URIに使用できない文字を表現する
    • 現代的なWebサイトの多くは文字エンコーディングとしてUTF-8を採用しているため、URIをUTF-8で%エンコードするのが無難

↓「あ」の%エンコーディング結果

文字エンコーディング %エンコーディング結果
UTF-8 %E3%81%82
Shift_JIS %82%A0
EUC-JP %A4%A2

5章 URIの設計

「URIは変わらないべきである。変わらないURIこそが最上のURIである」

URIの設計指針

  • URIにプログラミング言語依存の拡張子を利用しない(.pl、.rb、.do、.jspなど)
  • URIに実装依存のパス名を利用しない(cgibin、servletなど)
  • URIにプログラミング言語のメソッド名を利用しない
  • URIにセッションIDを含めない
  • URIはそのリソースを表現する名詞である

URIのユーザビリティ

  • シンプルなURIは変更しにくくなるだけでなく、ユーザビリティが高まる
  • シンプルなURIは可読性が高いため、ユーザがURIの構造を推測しやすくなる
# 複雑なURI
http://example.jp/servlet/LoginServlet # 意味が分かりにくい

# シンプルなURI
http://example.jp/login # 文字数が少なく覚えやすい

URI設計のテクニック

拡張子で表現を指定する

URIの設計指針にあるように、プログラミング言語依存の拡張子などはダメだが、以下のような実装に依存しない拡張子はOK

# プレスリリース(日本語版)
http://example.jp/2010/05/01/press.ja
# プレスリリース(英語版)
http://example.jp/2010/05/01/press.en

# HTML
http://example.jp/test.html
# JSON
http://example.jp/test.json

マトリクスURI

  • マトリクスURI
    • 階層構造を表現する/の代わりに、複数の軸のパラメータをそれぞれ;(または,)で区切ってリソースを表現する方法
      • ;→パラメータの順序が意味を持たない場合に使う
      • ,→パラメータの順序が意味を持つ場合に使う
    • 地図などのような、複数次元を持つ階層で表現できない情報に使う
# Google Mapsなどの緯度(Lat)と経度(Lng)を使って特定の場所を表現するURI
http://example.jp/map/lat=35.705471;lng=139.751898

# パラメータの順序で緯度・経度を指定する場合
http://example.jp/map/35.705471,139.751898

URIの不透明性

URI Opacity - W3C

  • URIをクライアント側で組み立てたり、拡張子からリソースの内容を推測したりできないことを、「URIはクライアントにとって不透明である」と言う
    • クライアントはあくまでもサーバが提供するURIをそのまま扱うだけにする
  • クライアントを作る際は、URIが不透明であることを心がける
    • URIの不透明性がなくなると、サーバ側の実装でURIの構造を変更したとたんにシステムが動かなくなってしまう、いわゆる密結合状態になってしまうため

6章 HTTPの基本

  • HTTP(Hypertext Transfer Protocol)
    • RESTの重要な特徴である統一インタフェース、ステートレスサーバ、キャッシュなどを実現している、Webの基盤となるプロトコル
    • TCP/IPをベースとしたプロトコル
    • HTMLやXMLなどのハイパーテキストだけではなく、静止画、音声、動画、JavaScriptプログラム、PDFや各種オフィスドキュメントファイルなど、コンピュータで扱えるデータであれば何でも転送できる

リクエストとレスポンス

  • HTTPはリクエスト/レスポンス型のプロトコル
    • クライアントが出したリクエストをサーバで処理してレスポンスを返す

クライアントで行われること

  • 1.リクエストメッセージの構築
  • 2.リクエストメッセージの送信
  • 3.(レスポンスが返るまで待機)
  • 4.レスポンスメッセージの受信
  • 5.レスポンスメッセージの解析
  • 6.クライアントの目的を達成するために必要な処理
    • ブラウザであればHTMLをレンダリングしてウィンドウに表示する処理
    • 検索エンジン用にデータを集めるロボットプログラムであればHTMLの解析結果をデータベースに格納する処理

サーバで行われること

  • 1.(リクエストの待機)
  • 2.リクエストメッセージの受信
  • 3.リクエストメッセージの解析
  • 4.適切なアプリケーションプログラムへの処理の委譲
    • クライアントがブラウザの場合、HTMLをレンダリングするアプリケーションに処理を委譲
  • 5.アプリケーションプログラムから結果を取得
    • 結果のHTMLを取得
  • 6.レスポンスメッセージの構築
    • 適切なヘッダを付加してレスポンスメッセージを構築
  • 7.レスポンスメッセージの送信

HTTPメッセージ

  • HTTPメッセージ
    • リクエストメッセージとレスポンスメッセージの総称

HTTPメッセージの構造

# スタートライン
# ヘッダ
# 空行
# ボディ
  • スタートライン
    • リクエストメッセージの場合、リクエストライン
    • レスポンスメッセージの場合、ステータスライン
  • ヘッダ
    • ヘッダの終了は空行で識別する
    • ヘッダは省略可
  • ボディ
    • ボディはテキストだけでなく、バイナリデータも入れられる
    • ボディは省略可

リクエストメッセージ

ex)http://example.jp/testに対する必要最小限のリクエスト

GET /test HTTP/1.1
Host: example.jp
  • リクエストライン
    • メソッド(GET)、リクエストURI(/test)、プロトコルバージョン(HTTP/1.1)から成る
    • リクエストラインにはURIフラグメントを除いたパス以降の文字列が入る(URIフラグメントはクライアント側で処理するのでリクエストメッセージには含めない)
    • リクエストURIはパス以降の文字列か絶対URI
  • ヘッダ
    • 各ヘッダは「名前:値」という構成
    • HTTP/1.1の場合、Hostヘッダは必須
  • ボディ
    • ボディには、そのメッセージを表す本質的な情報が入る
    • リソースを新しく作成したり更新したりするときは、リクエストのボディにリソースの表現そのものが入る

レスポンスメッセージ

ex)http://example.jp/testへのリクエストが成功した時のレスポンス

HTTP/1.1 200 OK
Content-Type: application/xhtml+xml; charset=utf-8

<html xmlns="http://www.w3.org/1999/xhtml">
...
</html>
  • ステータスライン
    • プロトコルバージョン(HTTP/1.1)、ステータスコード(200)、テキストフレーズ(OK)から成る
  • ヘッダ
    • この例では、Content-TypeヘッダでHTMLのMIMEメディアタイプ(application/xhtml+xml)と、その文字エンコーディング方式(utf-8)を指定
  • ボディ
    • この例ではボディにHTMLが含まれている

HTTPのステートレス性

yohei-y:weblog ステートレスとは何か

  • ステートレスの利点
    • ステートレスなサーバはアプリケーション状態を覚える必要がないため、サーバ側のシステムは単純になる
      • システムをスケールさせることが簡単になる(クライアントが増えてきたら単純にサーバを増設すれば良い)
    • 処理に必要な情報は全てリクエストに含まれるため、クライアントはどのサーバにリクエストを送っても良い
  • ステートレスの欠点
    • パフォーマンスの低下
      • 送信するデータ量が多くなる
      • 認証など、サーバに負荷がかかる処理を繰り返す
    • 通信エラーへの対処

7章 HTTPメソッド

HTTPメソッドの種類

メソッド 意味
GET リソースの取得
POST 子リソースの作成、リソースへのデータの追加、その他の処理
PUT リソースの更新、リソースの作成
DELETE リソース削除
HEAD リソースのヘッダ(メタデータ)の取得
OPTIONS リソースがサポートしているメソッドの取得
TRACE 自分宛にリクエストメッセージを返す(ループバック)試験
CONNECT プロキシ動作のトンネル接続への変更
PATCH リソースの部分更新

HTTPメソッドとCRUD

CRUD名 意味 メソッド
Create 作成 POST/PUT
Read 読み込み GET
Update 更新 PUT
Delete 削除 DELETE

1. GET(リソースの取得)

  • 指定したURIの情報を取得する
  • 最も利用頻度の高いメソッド
  • Webページの取得、画像の取得、映像の取得、フィードの取得などブラウザ利用時は多くのGETを発行している

ex) http://example.jp/listに対するGET

# リクエスト
GET /list HTTP/1.1
Host: example.jp

# レスポンス
HTTP/1.1 200 OK
ContentType: application/json
[
 {"uri": "http://example.jp/list/item1"},
 {"uri": "http://example.jp/list/item2"},
 {"uri": "http://example.jp/list/item3"},
 {"uri": "http://example.jp/list/item4"}
]

2. POST(リソースの作成、追加)

  • 子リソースの作成
  • リソースへのデータの追加
  • 他のメソッドでは対応できない処理

子リソースの作成

  • ブログ記事の投稿などの操作で使われる

ex) http://example.jp/listに対して新しい子リソースを作成するPOST

# リクエスト
POST /list HTTP/1.1
Host: example.jp
ContentType: text/plain; charset=utf-8

こんにちは! # ボディに新しく作成するリソースを入れる

# レスポンス
HTTP/1.1 201 Created # リソースを新たに作成したことを示す201が返る
ContentType: text/plain; charset=utf-8
Location: http://example.jp/list/item5 # Locationヘッダに新しいリソースのURIが入る

こんにちは!

リソースへのデータの追加

ex) http://example.jp/logに対して新しいログ(CSV形式)を追加するPOST

# リクエスト
POST /log HTTP/1.1
Host: example.jp

2010-10-10T10:13:00Z, GET /log, 200

# レスポンス
HTTP/1.1 200 OK # リソースの作成ではなくデータの追加の場合、200が返る
  • データ追加としてPOSTしたときに、そのデータをリソースの末尾に追加するのか先頭に追加するのかはサーバ側の実装に依存する
  • あるリソースへのPOSTが作成を意味するのかデータ追加を意味するのかも実装に依存する(URIを見ただけではPOSTの挙動はわからない)

他のメソッドでは対応できない処理

  • 例として「検索結果を表現するURIのキーワードが長い場合」が挙げられる
http://example.jp/search?q={キーワード}

通常は上記のURIをGETすることで検索を実行する

しかし長いキーワードの場合URIにキーワードを入れてGETする方式は利用できない(URIの仕様上は長さ制限はないが、実装上は2000文字などの上限が存在するため)

この場合、以下のようにPOSTを用いる

POST /search HTTP/1.1
Content-Type: application/x-www-form-urlencoded

q=very+long+keyword+foo+bar+..........

GETではURIに含めていたキーワードを、POSTではリクエストボディに入れられることによりどんなに長いキーワードでも実現できる

POSTでPUT/DELETEを代用する方法

  • GETとPOSTだけしか使えない状況
    • XMLHttpRequestをサポートしない携帯電話向けブラウザはフォームしか利用できないため、GETとPOST以外は使えない
    • セキュリティ上の理由から、プロキシサーバでGETとPOST以外のアクセスを制限している場合

このような状況でサーバにPUTやDELETEを伝える手法が以下の2つ

  1. _methodパラメータ
  2. X-HTTP-Method-Override

1. _methodパラメータ

  • フォームの隠しパラメータ(hidden)に_methodというパラメータを用意し、そこに本来送りたかったメソッドの名前を入れる方法
  • _methodパラメータはRuby on Railsが採用している

以下のフォームがある

<form method="POST" action="/list/item1">
  <input type="hidden" id="_method" name="_method" value="PUT"/>
  <textarea id="body">...</textarea>
</form>

このフォームを送信すると、以下のリクエストが送られる

POST /list/item1 HTTP/1.1
Host: example.jp
Content-Type: application/x-www-form-urlencoded

_method=PUT&body=...

Webアプリケーションフレームワークなどのサーバ側の実装は、_methodパラメータを見て、このリクエストをPUTとして扱う

2. X-HTTP-Method-Override

  • POSTの内容がXMLなど、application/x-www-form-urlencoded以外の場合は_methodパラメータは利用できないため、その場合に使う手法
  • GoogleのGData(Google Data Protocol)が採用している手法
POST /list/item1 HTTP/1.1
Host: example.jp
Content-Type: application/xml; charset=utf-8
X-HTTP-Method-Override: PUT

<body>...</body>

Webアプリケーションフレームワークなどのサーバ側の実装は、X-HTTP-Method-Overrideヘッダを見て、このリクエストをPUTとして扱う

3. PUT(リソースの更新、作成)

  • リソースの更新
  • リソースの作成

リソースの更新

ex)先程POSTで作成したitem5(http://example.jp/list/item5)を、PUTを使って「こんばんは!」に更新

# リクエスト
PUT /list/item5 HTTP/1.1
Host: example.jp
Content-Type: text/plain; charset=utf-8

こんばんは!

# レスポンス
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

こんばんは!

リソースの作成

ex)http://example.jp/newitemがまだ存在しない場合

# リクエスト
PUT /newitem HTTP/1.1
Host: example.jp
Content-Type: text/plain; charset=utf-8

新しいリソース/newitemの内容

# レスポンス
HTTP/1.1 201 Created
Content-Type: text/plain; charset=utf-8

新しいリソース/newitemの内容
  • POSTの場合は新しく作成したリソースのURIがLocationヘッダで返ったが、PUTの場合はクライアントがすでにリソースのURIを知っているためLocationヘッダを返す必要はない
  • /newitemがすでに存在していた場合は、先述したリソースの更新処理になる

POSTとPUTの使い分け

  • POSTでリソースを作成する場合、リソースのURIはサーバが決定する
    • TwitterのようにつぶやきのURIをサーバ側で自動的に決定するWebサービスの場合は、POSTを用いるのが一般的
  • PUTでリソースを作成する場合、リソースのURIはクライアントが決定する
    • Wikiのようにクライアントが決めたタイトルがそのままURIになるWebサービスの場合は、PUTを使うほうが適している
    • ただしPUTの場合、リソースの上書きを避けるためにクライアントで事前にURIの存在をチェックしなければならない

特別な理由がない限りは、リソースの作成はPOSTで行いURIもサーバ側で決定する設計が望ましい

4. DELETE(リソースの削除)

  • 指定したURIのリソースを削除する
  • 一般的にDELETEのレスポンスはボディを持たない。そのためレスポンスのステータスコードにはボディがないという意味の204 NoContentが使われる場合もある

ex) http://example.jp/list/item2に対するDELETE

# リクエスト
DELETE /list/item2 HTTP/1.1
Host: example.jp

# レスポンス
HTTP/1.1 200 OK

5. HEAD(リソースのヘッダの取得)

  • GETがリソースを取得するメソッドなのに対し、HEADはリソースのヘッダ(メタデータ)だけを取得するメソッド
  • HEADへのレスポンスにはボディが含まれないため、ネットワークの帯域を節約しながらリソースの大きさを調べたり、リソースの更新日時を取得したりできる

ex) http://example.jp/list/item1に対するHEAD

# リクエスト
HEAD /list/item1 HTTP/1.1
Host: example.jp

# レスポンス
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

6. OPTIONS(リソースがサポートしているメソッドの取得)

  • リソースがサポートしているメソッドの一覧を返す

ex) http://example.jp/listに対するOPTIONS

# リクエスト
OPTIONS /list HTTP/1.1
Host: example.jp

# レスポンス
HTTP/1.1 200 OK
Allow: GET, HEAD, POST

冪等性と安全性

  • 冪等(べきとう)(Idempotence)
    • 「ある操作を何回行っても結果が同じこと」を意味する数学用語
  • 安全(safe)
    • 「操作対象のリソースの状態を変化させないこと」(「操作対象のリソースに副作用がないこと」)
    • リソースの状態を変化させることを副作用と言う

HTTPメソッドの性質

メソッド 性質
GET、HEAD 冪等かつ安全
PUT、DELETE 冪等だが安全でない
POST 冪等でも安全でもない
  • GETとHEAD
    • 何回行っても結果は変わらず(冪等)、リソースの状態を変化させない(安全)
  • PUT
    • 何回行っても同じ値で更新する(冪等)、更新によりリソースの状態は変化する(安全でない)
  • DELETE
    • 何回行ってもリソースが削除されているという結果は同じ(冪等)、削除によりリソースの状態は変化する(安全でない)
  • POST
    • 同じリクエストを2度送ると、2個のリソースが作成される(冪等でない)、作成によりリソースの状態は変化する(安全でない)

8章 ステータスコード

HTTP レスポンスステータスコード - HTTP | MDN

ステータスコードの分類と意味

ステータスコードは3桁の数字であり、先頭の数字によって次の5つに分類される

  • 1xx:処理中
    • 処理が継続していることを示す
    • クライアントはそのままリクエストを継続するか、サーバの指示に従ってプロトコルをアップデートして再送信する
  • 2xx:成功
    • リクエストが成功したことを示す
  • 3xx:リダイレクト
    • ほかのリソースへのリダイレクトを示す
    • クライアントはこのステータスコードを受け取ったとき、レスポンスメッセージのLocationヘッダを見て新しいリソースへ接続する
  • 4xx:クライアントエラー
    • クライアントエラーを示す
    • エラーを解消しない限り正常な結果が得られないので、同じリクエストをそのまま再送信することはできない
  • 5xx:サーバエラー
    • サーバエラーを示す
    • サーバ側の原因が解決すれば、同一のリクエストを再送信して正常な結果が得られる可能性がある

よく使われるステータスコード

200 OK - リクエスト成功

  • 200 OKはリクエストが成功したことを示す
  • GETの場合はボディにリソースの表現が入る
  • PUTやPOSTの場合はボディに処理結果が入る

201 Created - リソースの作成成功

  • 201 Createdはリソースを新たに作成したことを示す
  • POSTとPUTのレスポンスとして返る
  • POSTの場合、新しく作成したリソースのURIはレスポンスのLocationヘッダに絶対URIとして入る
  • PUTの場合はクライアントが新しいリソースのURIを知っているためLocationヘッダは入らない

301 Moved Permanently - リソースの恒久的な移動

  • 301 Moved Permanentlyは、リクエストで指定したリソースが新しいURIに移動したことを示す
  • 古いURIを保ちつつ、新しいURIに移行する際にこのステータスコードを用いる
  • 新しいURIはレスポンスのLocationヘッダに絶対URIとして入る

303 See Other - 別のURIの参照

  • 303 See Otherは、リクエストに対する処理結果が別のURIで取得できることを示す
  • 典型的にはブラウザからPOSTでリソースを操作した結果をGETで取得するときに使う

400 Bad Request - リクエストの間違い

  • 400 Bad Requestは、リクエストの構文やパラメータが間違っていたことを示す
  • 400 Bad Requestは、ほかに適切なクライアントエラーを示すステータスコードがない場合にも用いられる
  • クライアントにとって未知の4xx系ステータスコードが返ってきた場合、400 Bad Requestと同じ扱いで処理するよう仕様で定められている

401 Unauthorized - アクセス権不正

  • 401 Unauthorizedは、適切な認証情報を与えずにリクエストを行ったことを示す
  • レスポンスのWWW-Authenticateヘッダで、クライアントに対して認証方式を伝える

404 Not Found - リソースの不在

  • 404 Not Foundは、指定したリソースが見つからないことを示す
  • レスポンスボディにはその理由が入る

500 Internal Server Error - サーバ内部のエラー

  • 500 Internal Server Errorは、サーバ側に何らかの異常が生じていて、正しいレスポンスが返せないことを示す
  • レスポンスボディには異常の理由が入る
  • 500 Internal Server Errorは、ほかに適切なサーバエラーを示すステータスコードがない場合にも用いられる
  • クライアントにとって未知の5xx系ステータスコードが返ってきた場合、500 Internal Server Errorと同じ扱いで処理するよう仕様で定められている

503 Service Unavailable - サービス停止

  • 503 Service Unavailableは、サーバがメンテナンスなどで一時的にアクセスできないことを示す
  • レスポンスボディにはその理由が入る
  • レスポンスのRetry-Afterヘッダでサービス再開時期がおよそ何秒後であるかを通知できる

9章 HTTPヘッダ

  • HTTPヘッダはメッセージのボディに対する付加的な情報、いわゆるメタデータを表現する
  • クライアントやサーバはヘッダを見てメッセージに対する挙動を決定する

主なHTTPヘッダ

HTTP ヘッダー - HTTP | MDN

1. 日時

  • Date
    • メッセージを生成した日時
  • If-Modified-Since
    • 条件付きGETでリソースの更新日時を指定するときに利用する
  • If-Unmodified-Since
    • 条件付きPUTや条件付きDELETEでリソースの更新日時を指定するときに利用する
  • Expires
    • レスポンスをキャッシュできる期限
  • Last-Modified
    • リソースを最後に更新した日時
  • Retry-After
    • 再度リクエストを送信できるようになる日時の目安

2. MIMEメディアタイプ

MIME タイプ (IANA メディアタイプ) - HTTP | MDN

IANA - タイプ/サブタイプ一覧

  • MIMEメディアタイプ(Multipurpose Internet Mail Extensions)
    • メッセージでやりとりするリソースの表現の種類を指定する
  • MIMEメディアタイプの構造
    • タイプ/サブタイプ
  • Content-Type
    • そのメッセージのボディの内容がどのような種類なのかをメディアタイプで示す
    • Content-Type: タイプ/サブタイプ; 引数=値
      • Content-Type: application/xhtml+xml; charset=utf-8
    • charsetパラメータ - 文字エンコーディングを指定する
      • 上記の例のcharset=utf-8ではXHTML文書をUTF-8でエンコードしていることを示す
      • textタイプの場合、必ずcharsetパラメータを付ける
      • XML文書の場合、text/xmlを使わずにapplication/xmlapplication/xhtml+xmlのようなメディアタイプを利用し、かつ必ずcharsetパラメータを付ける

3. 言語タグ

  • Content-Language
    • リソース表現の自然言語を指定する
    • Content-Language: ja-JP
    • Content-Language: en-US

4. コンテントネゴシエーション

メディアタイプや文字エンコーディング、言語タグは、サーバが一方的に決定するだけではなく、クライアントと交渉(ネゴシエーション)して決めることもでき、その手法を「コンテントネゴシエーション」と呼ぶ

  • Accept
    • クライアントが自分の処理できるメディアタイプをサーバに伝える
    • Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
      • text/htmlとapplication/xhtml+xmlがデフォルトの1、application/xmlが0.9、それ以外の全てのメディアタイプ(*/*)が0.8という優先度
      • クライアントがAcceptヘッダで指定したメディアタイプにサーバが対応していなかった場合は、406 Not Acceptableが返る
  • Accept-Charset
    • クライアントが自分の処理できる文字エンコーディングをサーバに伝える
    • Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
      • Shift_JISがデフォルトの1、UTF-8とそれ以外の全てのエンコーディング方式が0.7の優先度
  • Accept-Language
    • クライアントが自分の処理できる言語タグをサーバに伝える
    • Accept-Language: ja,en-us;q=0.7,en;q=0.3
      • 日本語(ja)がデフォルトの1、アメリカ英語(en-us)が0.7、地域を特に指定しない英語(en)が0.3という優先度

5. Content-Lengthとチャンク転送

  • Content-Length
    • メッセージがボディを持っている場合、そのサイズを10進数のバイトで示す
    • あらかじめサイズのわかっているリソースを転送する場合に使う
  • Transfer-Encoding
    • 最終的なサイズがわからないボディを少しずつ転送するときに使う(動的に画像を生成するようなWebサービスなど)
    • Transfer-Encoding: chunked

6. 認証

WWW-Authenticate - HTTP | MDN

Authorization - HTTP | MDN

  • HTTP1.1が規定している認証方式には、Basic認証とDigest認証がある
  • あるリソースにアクセス制御がかかっている場合、ステータスコード401 UnauthorizedとWWW-Authenticateヘッダを利用して、クライアントにリソースへのアクセスに必要な認証情報を通知できる

例:Basic認証をサポートしているサーバへのリクエスト(認証に失敗)

# リクエスト
DELETE /test HTTP/1.1
Host: example.jp

# レスポンス
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Example.jp"
  • WWW-Authenticateヘッダの値をチャレンジと呼ぶ
    • クライアントはチャレンジを使って次回のリクエストを組み立てる(サーバが提供する認証方式を理解し、その方式に従った形で認証情報を送る)
  • realmは、サーバ上でこのリソースが属しているURI空間の名前
  • 401 Unauthorizedはパスワードを間違えた場合など実際に認証できなかった場合にも返る
Basic認証
  • ユーザ名とパスワードによる認証方式
  • ユーザ名とパスワードはAuthorizationヘッダに入れてリクエストごとに送信する
# リクエスト
DELETE /test HTTP/1.1
Host: example.jp
Authorization: Basic dXNlcjpwYXNzd29yZA==
  • Authorizationヘッダの内容は、認証方式(Basic)に続けて、ユーザ名とパスワードを「:」で連結しBase64エンコードした文字列を入れる(上記の例はuser:password)
    • Base64エンコードは簡単にデコード可能であり、ユーザ名とパスワードが平文でネットワーク上を流れていることと変わらない(HTTPSで暗号化の必要あり)
Digest認証
  • Basic認証よりセキュアな認証方式
  • サーバ上にパスワードそのものではなく、パスワードのハッシュ値を保管しておけばよいため、パスワード漏洩のセキュリティリスクが下がる
  • メッセージ自体は平文でネットワーク上を流れるため、メッセージを暗号化したい場合は、Basic認証の場合と同様にHTTPSを利用する
  • ApacheなどのWebサーバではDigest認証がオプション扱いのため、ホスティングサービスではサポートしていない可能性がある

チャレンジ

# リクエスト
DELETE /test HTTP/1.1
Host: example.jp

# レスポンス
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="Example.jp", nonce="1ac421d9e0a4k7q982z966p903372922", qop="auth", opaque="92eb5ffee6ae2fec3ad71c777531578f"
  • nonce
    • number used once(一度だけ使われる数字)の略で、リクエストごとに変化する文字列
    • 基本的にはタイムスタンプやサーバだけが知り得るパスワードから生成する
  • qop
    • quality of protection(保証の品質)の略で、authかauth-initを指定する
    • authの場合はメソッドとURIからダイジェストを作成するのに対し、auth-initの場合はメソッドとURIに加えてメッセージボディも利用する
      • つまり、POSTやPUTでボディを送信するときは、auth-initを使うとメッセージ全体が改ざんされていないことを保証できる(完全性保護付きの認証:Authentication with integrity protection)
  • opaque
    • サーバが指定する文字列で、クライアントは推測できない(不透明)
    • 同じURI空間へのリクエストでは、 クライアントはAuthorization ヘッダで変更せずに返す

ダイジェストの生成と送信

# リクエスト
DELETE /test HTTP/1.1
Host: example.jp
Authorization: Digest username="testuser", realm="Example.jp", nonce="1ac421d9e0a4k7q982z966p903372922", uri="/test", qop="auth", nc=00000001, cnonce="900150983cd24fb0d6963f7d28e17f72", response="0fde218e18949a550985b3a034abcbd9", opaque="92eb5ffee6ae2fec3ad71c777531578f"
  • サーバから認証に必要な情報を得たクライアントは、自分のユーザ名とパスワードを使ってダイジェストを生成する
  • クライアントは、生成したダイジェスト値をresponseというフィールドに入れてリクエストを送信する

7. キャッシュ

キャッシュとは、サーバから取得したリソースをローカルストレージ(ハードディスクなど)に蓄積し、再利用する手法のこと(キャッシュしたデータそのもののこともキャッシュと呼ぶ)

あるリソースがキャッシュ可能かどうかは、そのリソースを取得したときのヘッダで判断する

  • Pragma
    • Pragmaヘッダを持つリソースはキャッシュされない
    • 指定できる値はno-cacheのみ
  • Expires
    • キャッシュの有効期限を絶対時間で示す
    • 仕様では最長で約1年の期限が推奨される
  • Cache-Control
    • PragmaヘッダとExpiresヘッダの機能を代用でき、より細かくキャッシュを制御できる
    • HTTP 1.1から追加
    • Cache-Control: no-cache
    • Cache-Control: max-age: 86400
      • 現在からの相対時間で有効期限を設定できる
  • キャッシュ用ヘッダの使い分け
    • キャッシュをさせない場合は、PragmaとCache-Controlのno-cacheを同時に指定する
    • キャッシュの有効期限が明確に決まっている場合は、Expiresを指定する
    • キャッシュの有効期限を相対的に指定したい場合は、Cache-Controlのmax-ageで相対時間を指定する
条件付きGET

HTTP 条件付きリクエスト - HTTP | MDN

304 Not Modified - HTTP | MDN

  • 条件付きGET
    • サーバ側にあるリソースが、クライアントローカルのキャッシュから変更されているかどうかを調べるヒントをリクエストヘッダに含めることで、キャッシュがそのまま使えるかどうかを検証するしくみ
    • そのリソースがLast-ModifiedヘッダまたはETagヘッダを持っているときに利用できる

If-Modified-Since - リソースの更新日時を条件にする

# リクエスト
GET /test HTTP /1.1
Host: example.jp
If-Modified-Since: Thu, 11 May 2010 16:00:00 GMT

# レスポンス
HTTP/1.1 304 Not Modified
Content-Type: application/xhtml+xml; charset=utf-8
Last-Modified: Thu, 11 May 2010 16:00:00 GMT
  • リソースの更新日時はLast-Modifiedヘッダで確認できる
  • 304 Not Modifiedは、条件付きGETへのレスポンスで、サーバ上のリソースを変更していないことを知らせるステータスコード
    • リクエストされたリソースを再送する必要がないことを示す
    • つまり上記の例では指定の日時以降にリソースが変更されていないので、クライアントはキャッシュを再利用できる
    • Last-Modifiedの日時がIf-Modified-Sinceで指定した日時よりも進んでいれば、200 OK ステータスと共に、要求したリソースを送信する
  • 時計を持っていないサーバや、ミリ秒単位で変更される可能性のあるリソースには利用できない

If-None-Match - リソースのETagを条件にする

# リクエスト
GET /test2 HTTP/1.1
Host: example.jp
If-None-Match: ab3322028

# レスポンス
HTTP/1.1 304 Not Modified
Content-Type: application/xhtml+xml; charset=utf-8
ETag: ab3322028
  • ETagの値とIf-None-Matchの値が異なっていれば(None Matchなら)、200 OK ステータスと共に、要求したリソースを送信する
  • ETag(エンティティタグ)はリソースの更新状態を比較するためだけに使う文字列
    • リソースを更新したときに別の値になるのであれば、どのような文字列でもかまわない

If-Modified-SinceとIf-None-Matchの使い分け

  • クライアントの立場では、サーバがETagヘッダを出している場合はIf-None-Matchヘッダを利用するほうが良い
    • Last-Modifiedヘッダよりも正確な更新の有無が確認できるため
  • サーバ側を実装する場合は、キャッシュ可能なリソースにはできるだけETagヘッダを利用する
  • ETagヘッダがなくLast-Modifiedヘッダしかわからない場合に、If-Modified-Sinceヘッダを使う

8. 持続的接続

  • 持続的接続
    • クライアントとサーバの間でリクエストのたびに切断するのではなく、まとめて接続し続ける手法
    • HTTP 1.0では、Keep-Aliveヘッダで実現
    • HTTP 1.1では、持続的接続がデフォルトの動作になった
  • パイプライン化
    • クライアントはレスポンスを待たずに同じサーバに複数のリクエストを送信できる
    • コネクションを切断したい場合は、リクエストでConnectionヘッダにcloseという値を指定する

9. その他のHTTPヘッダ

  • Content-Disposition
    • サーバがクライアントに対してそのリソースのファイル名を提示するために利用するレスポンスヘッダ
    • Content-Disposition: attachment; filename="rest.txt"
  • Slug
    • ファイル名のヒントを指定する