- Published on
コンテンツネゴシエーション徹底解説
- Authors
- Name
- あやき
- @ayaki2325
はじめに
対象読者は、コンテンツネゴシエーションを知らないバックエンドエンジニアです。
この記事のゴールは、単一のリソースに対して複数の表現を提供できるようになることです。
コンテンツネゴシエーションとは
コンテンツネゴシエーションは、同じURIにおけるさまざまな表現のリソース1を提供する仕組みです。
一つのURLでJSONやXMLといった複数のフォーマットのデータを返却できるWeb APIを見たことはありませんか?
同じURLであっても「JSONを希望する人にはJSON」「XMLを希望する人にはXML」というふうに返すコンテンツを制御することが可能です。
これを実現する仕組みがコンテンツネゴシエーションです。
具体的には、Webブラウザなどのクライアントが「JSONのコンテンツが欲しい」という希望をサーバーに伝え、サーバーがその希望を考慮して最適なコンテンツを提供するというやり取りを通して実現します。
次に、コンテンツネゴシエーションの具体的な仕組みを解説します。
コンテンツネゴシエーションの仕組み
コンテンツネゴシエーションは、HTTPヘッダーを使ったクライアントとサーバー間の対話で成り立っています。
このやり取りを通して、サーバーはクライアントの希望するコンテンツを考慮してレスポンスできるのです。
クライアントが「希望」を伝えるリクエストヘッダー
クライアントはリクエストの際にAccept
から始まるHTTPヘッダーを使用し、自身の希望するコンテンツとその優先度をサーバーに伝えます。
Accept
クライアントが処理できるメディアタイプとその優先度を伝えます。
以下のようにカンマ,
区切りで指定します。
また、優先度は0.0から1.0の間の数値でq
を使って表現します。
1.0が優先度最大で、省略した場合はq=1.0
と同じ意味になります。
こうしたフォーマットは、他のAccept-*
でも同様です。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset
クライアントが処理できる文字セットとその優先度を伝えます。
Accept-Charset: iso-8859-5,unicode-1-1;q=0.8
Accept-Encoding
クライアントが処理できるコンテンツコーディングとその優先度を伝えます。
Accept-Encoding: gzip,deflate
Accept-Language
クライアントが処理できる自然言語とその優先度を伝えます。
Accept-Language: ja,en-US;q=0.7,en;q=0.3
サーバーが「決定」を伝えるレスポンスヘッダー
サーバーはリクエストを解釈し、返却するコンテンツがどのようなものかを示すために、Content
から始まるHTTPヘッダーを使用します。
Content-Encoding
サーバーがコンテンツに対して施したコンテンツコーディングの形式を伝えます。
Content-Encoding: gzip
Content-Language
サーバーがコンテンツに使用されている自然言語を伝えます。
Content-Language: ja
Content-Type
サーバーがコンテンツのメディアタイプと文字セットを伝えます。
Content-Type: text/html;charset=UTF-8
Varyヘッダーでキャッシュ汚染を防ぐ
コンテンツネゴシエーションを実装する上で、Varyレスポンスヘッダーの存在を忘れてはいけません。
キャッシュサーバーは、通常URLだけでコンテンツをキャッシュします。
同一URLでJSONとXMLを返せるようにした場合、両者を区別せずキャッシュします。
ということは、JSONフォーマットのレスポンスがキャッシュされたとき、次にXMLを希望するクライアントにもJSONが誤って返されてしまう可能性があるのです。
これをキャッシュ汚染と呼びます。
Varyヘッダーは、どのリクエストヘッダーの値によってレスポンスの内容が変わるのかをキャッシュサーバーに伝えます。
URLだけではなく、特定のリクエストヘッダーの値も考慮に入れてキャッシュしてくれるよう指定できるのです。
Vary: Accept
今回の例だとこのように指定することで、キャッシュサーバーはURLだけでなく、Acceptヘッダーの値も考慮してキャッシュを保存・提供するようになり、キャッシュ汚染を防ぐことができます。
Varyヘッダーについては、以下でも解説してあるので、気になる人はぜひご覧ください。
コンテンツネゴシエーションの主な種類
コンテンツネゴシエーションには主に2つの方式があり、それぞれにメリットとデメリットが存在します。
- サーバー駆動型ネゴシエーション: サーバーが
Accept-*
ヘッダーを解釈し、最適な表現を自動的に選択して返します。1回の通信で完結するため高速ですが、サーバー側の実装が複雑になりがちです。 - エージェント駆動型ネゴシエーション: サーバーが
300 Multiple Choices
というステータスコードと共に、選択肢のリスト(例: 日本語版はこちら、英語版はこちら)を返却し、最終的な選択をクライアントに委ねる方式です。サーバー側の実装がシンプルになりますが、複数の通信が発生するので遅くなります。
コンテンツネゴシエーションのユースケース
コンテンツネゴシエーションが関連する代表的なユースケースを紹介します。
ユースケース1: APIのバージョン管理
APIのバージョン管理には主に2つのアプローチがあります。
Acceptヘッダーを用いた方法
URLには/api/users
のようにバージョン情報を持たせず、クライアントがカスタムメディアタイプでバージョンを指定します。
Accept: application/vnd.myapi.v2+json
RESTの思想に忠実な一方で、リクエストを送るのが少し煩雑になります。
URIパスにバージョンを含める方法
URLに/api/v2/users
のようにバージョンを含めます。
直感的でわかりやすい一方で、RESTの思想と外れてしまいます。
ユースケース2: Webサイトの多言語対応
Accept-Languageヘッダーで同一URLに対して複数言語のコンテンツを返すことは可能ですが、今は一般的ではなくなってきました。
Googleの他地域、多言語のサイトの管理によれば、言語ごとに個別のURL(例:https://example.com/ja/
、https://example.com/en/
)を用意し、それらをhreflang
タグで関連付けることが推奨されています。
また、ユーザーが自分で求める言語を選択できるように、手動の言語スイッチを設置するなどの配慮も当たり前になされるようになってきました。
ユースケース3: 次世代フォーマットの配信
Acceptヘッダを見て、対応ブラウザにだけ新しい画像・動画フォーマットを配信するケースです。image/avif
やimage/webp
を処理可能なブラウザにこれらを返すようにすると、次世代フォーマットの利益をクライントが享受できます。
まとめ
- コンテンツネゴシエーションとは、クライアントとサーバーがHTTPヘッダーで対話し、同じURLでユーザーに最適なコンテンツを届ける仕組み
- 実装の際は、キャッシュ汚染を防ぐVaryヘッダーの指定が不可欠
- コンテンツネゴシエーションには、サーバー駆動型ネゴシエーションとエージェント駆動型ネゴシエーションがある
- APIのバージョン管理、SEOを考慮した多言語対応、次世代フォーマット配信など、バックエンド開発における応用シーンは多岐にわたる
参考文献
Footnotes
HTTPの仕様では、
/users/1
のようなURIが指す抽象的な概念を「リソース」と呼びます。それに対し、そのリソースを具体的なHTML、JSON、あるいは日本語版や英語版といった形で具体化したものを「表現」と呼びます。この記事ではわかりやすさを優先して、以降の説明では両者を厳密に使い分けず単に「コンテンツ」という言葉を使います。厳密な"表現"はここでは控えます(ほら、ややこしいでしょう?)。 ↩