Content Negotiation

この頁では、HTTPを使って「表現」を自動選択する機能である、内容ネゴシエーション(コンテントネゴシエーション)について解説します。

内容ネゴシエーション(コンテントネゴシエーション)とは

はじめに

内容ネゴシエーション(※)とは、クライアントサーバが文字通りそのリソースの「内容」について「交渉」をすることで、クライアントが最も望む形態のリソースを得られる様にするための仕組みです。 RFC 2616のsection 12では、内容ネゴシエーションに関する章です。

(※) 内容ネゴシエーション{Content Negotiation}は、英語をそのまま読んだ「コンテントネゴシエーション」と書かれる場合も多いですが、当サイトでは「内容ネゴシエーション」という呼び名で統一します。

多くの HTTP レスポンスは、人間ユーザによって解釈される情報から成るエンティティを含む。 当然、リクエストに対応した "最も利用できる" エンティティをユーザに供給するのが望ましい。 サーバやキャッシュにとって不幸な事は、すべてのユーザが "最上" なものについて同じ優先度を持たせているわけではないし、すべてのユーザエージェントがすべてのエンティティタイプを等しく表現できるわけではないという事である。 この理由により、HTTP は "内容ネゴシエーション"、すなわち複数の利用可能な表現がある時に与えられたレスポンスにとって最適な表現を選択するための処理のためのいくつかのメカニズムを持っている。

注意:これは、ある別の表現として、メディアタイプは同じであるが使用言語が異なっている等、そのタイプとは異なる能力{capabilities} を使用するかもしれないので、"フォーマットネゴシエーション" とは呼ばれない。

エンティティボディを含むあらゆるレスポンスは、エラーレスポンスを含めネゴシエーションを受けさせる事ができる

元々、内容ネゴシエーションは、HTTPの仕様とは別に研究されていました。 HTTP における透過的内容ネゴシエーションという文書が、最初のHTTP/1.1仕様書の発行後に、単独で発行されています。

web 基盤に内容ネゴシエーションを追加する事は、web の初期から重要であると考えられてきた。 内容ネゴシエーションのための十分に強力なシステムの期待される利点の中には以下のものがある。

HTTP/1.1 では、web サイトの作者が単一の URI に属する同じ情報の複数のバージョンを置く事を許している。 それらバージョンのそれぞれは `バリアント' と呼ばれる。 例えば、リソース http://x.org/paper は、文書の三つの異なるバリアントに結び付ける事ができる:

  1. HTML, English
  2. HTML, French
  3. Postscript, English

内容ネゴシエーションは、リソースがアクセスされた時に、最善のバリアントが選択される事による過程である。 この選択は、利用可能なバリアントの属性とユーザエージェントの能力やユーザの優先度が一致する事によって完了する。

たとえば、「日本語版」や「英語版」などの『リソースの言語が異なる』場合、あるいは HTML ファイルやプレーンテキスト、更にそれに gzip による圧縮等が施されているといった『メディアタイプが異なる』場合、これらのファイルのそれぞれはバリアント{variant}と呼ばれます。 内容ネゴシエーションとは、単一のURIに配置された複数のバリアントをサーバやユーザエージェントが自動に選択できるようにする仕組みです。

サーバ駆動型ネゴシエーション

サーバ駆動型ネゴシエーションと品質値

内容ネゴシエーションをサーバ側で行うことをサーバ駆動型ネゴシエーションと言い、section 12.1 に記述されています。

レスポンスとしての最適な表現の選択がサーバに設けられたアルゴリズムによって行われた場合、これはサーバ駆動型ネゴシエーションと呼ばれる。 選択は、利用可能なレスポンスの表現 (それが変化できる次元、例えば言語や内容コーディング等) や、リクエストメッセージ内の特定のヘッダフィールドの内容、あるいはそのリクエストに関するその他の情報 (例えばクライアントのネットワークアドレス) に基づく。

サーバ駆動型ネゴシエーションは、利用可能な表現の中から選択するためのアルゴリズムがユーザエージェントに説明するのが難しい時や、サーバが最初のレスポンスと一緒にクライアントに自身の "最適な推測" を送る事を望む (もしその "最適な推測" がユーザにとって十分なものであれば、以降のリクエストの往復時間遅れ{round-trip delay} を避けられる) 時に有益である。 サーバの推測を改善するため、ユーザエージェントはそのようなレスポンスのための自身の優先度を表すリクエストヘッダフィールド (Accept, Accept-Language, Accept-Encoding 等) を含む事ができる

ここでいう「表現」とは、先に挙げた言語やメディアタイプの違いなどを指しています。 もし、内容ネゴシエーションがなかったら、クライアントが受け取ったリソースについて、クライアントがより望む形の「表現」が存在すると知った場合、クライアントはそのリソースは廃棄し、再度サーバにリクエストをするでしょう。 サーバ駆動型ネゴシエーションはこの手間を省いてくれます。 結果的に、時間的・金銭的通信コストが節約され、またサーバの負荷も軽減できるということになるわけです。

では、リソースの「表現」に優先順位をつけたい場合、たとえば「image/pngがあればそのリソースを、なければimage/jpegのリソースをください」という場合、どうすればいいのでしょうか? このような場合、ユーザエージェントAcceptAccept-LanguageAccept-Encodingなどのヘッダに品質値というパラメータを追加することができます。 品質値については、RFC 2616のsection 3.9をご覧ください。

HTTP 内容ネゴシエーション (section 12) は、さまざまなネゴシエート可能なパラメータの相対な重要性 ("ウェイト") を示すために短い "浮動小数点" 数を使う。 ウェイトは、0 から 1 までの実数値と標準化され、0 は最小値で 1 は最大値である。 もしパラメータが 0 の品質値を持っていたら、そのパラメータと共にあるものはクライアントは「利用不可能」である。 HTTP/1.1 アプリケーションは、小数点以下で三桁を越える数字を生成してはならない。 これらの値のユーザ設定も、この様式にかぎられるべきである

 qvalue         = ( "0" [ "." 0*3DIGIT ] )
                | ( "1" [ "." 0*3("0") ] )

"品質値" とは、単に要請される特質の相対的な格付けを示すものなので、実際は誤った表現である。

では、実際にどのように使われているのかの例として、Acceptヘッダについて記述されている、RFC 2616のsection 14.1をご覧ください。

次の例を見よ。

 Accept: audio/*; q=0.2, audio/basic

この例では、「私は audio/basic を望むが、もし品質を 80% 下げても最も有効に利用できるのであれば、どんな audio タイプでもかまわない」と解釈されるべきである

Accept ヘッダフィールドが無い場合は、クライアントはすべてのメディアタイプを受けつけるとみなされる。 Accept ヘッダフィールドがある場合に、サーバが Accpet フィールド値に適したレスポンスを送る事ができなければ、サーバは 406 (not acceptable) レスポンスを返すべきである

より複雑な例を示す。

 Accept: text/plain; q=0.5, text/html,
         text/x-dvi; q=0.8, text/x-c

これを文字通りに解釈すると、「text/html と text/x-c が望むメディアタイプであるが、もし存在しないのであれば text/x-dvi エンティティを、それも無ければ、text/plain エンティティを送信せよ。」となる。

メディアレンジでは、より特定されるメディアレンジやメディアタイプが優先される。 あるメディアタイプに複数のメディアレンジが当てはまる場合、最も特定される指示を優先される。 例を見よ。

 Accept: text/*, text/html, text/html;level=1, */*

この優先順位は以下の様になる。

  1. text/html;level=1
  2. text/html
  3. text/*
  4. */*

あるメディアタイプに関連付けられる品質係数は、そのメディアタイプにマッチする最も高い優先度を持つメディアレンジを発見する事によって決定される。 例を見よ。

 Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1,
        text/html;level=2;q=0.4, */*;q=0.5

品質係数は以下の様になる。

 text/html;level=1         = 1
 text/html                 = 0.7
 text/plain                = 0.3
 image/jpeg                = 0.5
 text/html;level=2         = 0.4
 text/html;level=3         = 0.7

注: ユーザエージェントは、あるメディアレンジに対して既定の品質値を供給するかもしれない。 しかし、ユーザエージェントが他のレンダリングエージェントと相互作用できないクローズドなシステムで無いのであれば、この既定値はユーザが設定できるようにすべきである。

上はAcceptの例でしたが、これ以外のヘッダでも、品質値を使って、リソースに対する優先度を指定しています。

クライアント駆動型ネゴシエーション

ところで section 12.1 には続きがあります。それをご覧下さい。

サーバ駆動型ネゴシエーションは以下の不都合を持つ。

  1. ユーザエージェントの能力とレスポンスの使用目的 (例えば、ユーザはそれをスクリーンに表示させたいのか、それとも紙に印刷したいのか?) の両方の完全な情報が必要なので、サーバがそのユーザにとって "最適" であるものをすべて正確に決定するのは不可能である。
  2. リクエスト毎にユーザエージェントに自身の能力を記述させる事は、非常に能率が悪く (レスポンスが複数の表現を持っている確率は少ないため)、ユーザのプライバシーの潜在的な侵害となりうる。
  3. リクエストに対してレスポンスを生成するため、オリジンサーバの実装とそのアルゴリズムが複雑になってしまう。
  4. 複数のユーザのリクエストに同じレスポンスを使う共有キャッシュの能力を制限するかもしれない。

サーバ駆動型ネゴシエーションだけでは、最適な「表現」を選択する事は難しい場合があります。 そこで、クライアントにもネゴシエーションを行わせようと考えられ、これをクライアント駆動型ネゴシエーションと言い呼び、section 12.2 にて記述されています。

エージェント駆動型ネゴシエーションの場合、レスポンスとしての最適な表現の選択は、オリジンサーバからの最初のレスポンスを受けとった後にユーザエージェントによって実行される。 選択は、最初のレスポンスのヘッダフィールドやエンティティボディに含まれる利用可能なレスポンスの表現のリストに基づき、それぞれの表現毎に自身の URI によって識別される。 表現内からの選択は、 (ユーザエージェントがそうする能力があれば) 自動的に、あるいは生成された (おそらくハイパーテキストの) メニューからのユーザの選択によって手動的に行われるだろう。

エージェント駆動型ネゴシエーションは、レスポンスが一般的に使われる次元 (例えばタイプ、言語、エンコーディングのような) と異なる時、オリジンサーバがリクエストの評価からはユーザエージェントの能力を決定できない時、そして一般的に共有キャッシュがサーバの負荷を分散し、ネットワークの使用を減らすために使用される時に有益である。

エージェント駆動型ネゴシエーションは、最適な入れ替わるべき "表現" を得るために次のリクエストが必要となるという不都合がある。 次のリクエストはキャッシングが使用されている時にのみ効率的である。 さらに、この仕様書では自動選択をサポートするどんなメカニズムも拡張として改良したり、HTTP/1.1 内で使用したりする事を禁止しないが、そのいかなるメカニズムも定義しない。

HTTP/1.1 は、サーバがサーバ駆動型ネゴシエーションで使って異なるレスポンスを供給しようとしない、あるいはできない時にエージェント駆動型ネゴシエーションを可能にするため、300 (Multiple Choices) と 406 (Not Acceptable) ステータスコードを定義する。

例えば、日本語を用いたリソースしか用意していないサイトに、日本語を理解できないユーザエージェントがアクセスしてきた場合を考えます。

 GET /negotiation HTTP/1.1
 Host: www.studyinghttp.net
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9) Gecko/2008052906 Firefox/3.0
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 Accept-Language: en-us,en;q=0.5
 Accept-Encoding: gzip,deflate
 Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
 Keep-Alive: 300
 Connection: keep-alive
 Referer: http://www.studyinghttp.net/
 Cache-Control: max-age=0

 HTTP/1.x 406 Not Acceptable
 Date: Wed, 18 Jun 2008 11:51:26 GMT
 Server: Apache
 Alternates: {"negotiation.shtml" 1 {type text/html} {charset euc-jp} {language ja}}
 Vary: negotiate
 TCN: list
 Keep-Alive: timeout=1, max=9
 Connection: Keep-Alive
 Transfer-Encoding: chunked
 Content-Type: text/html; charset=iso-8859-1

リクエストの Accept-Language には、日本語を意味する“ja”が含まれていません。 しかし、このサイトには日本語のリソースしかなので、サーバは返すべきリソースが無いと判断しました(現在、サーバにある表現のリストは Alternates ヘッダに含むことができます) よって、このリクエストは無効であるとして、サーバはクライアントにリクエストの再実行を求めるために 406 (Not Acceptable) ステータスコードを返しています。

逆に、サーバが複数の「表現」を返しうる場合は、そのリクエストは有効であるが、クライアントに更なる動作を求めるために 300 (Muliple Choices) レスポンスと、各「表現」へのリンクがレスポンスボディとして返されます。

このように、ユーザに手動入力を求める画面で、ユーザエージェントが自動的に処理する事がクライアント駆動型ネゴシエーションなのです。

参照文献

Webリソース