Webプログラミング必見!HTTP「Accept」ヘッダーを使いこなして最適なコンテンツを配信


Accept ヘッダーの構文

Accept: [media type list] [;q=quality value]
  • q=quality value: 各コンテンツタイプの優先度を 0.0 から 1.0 の範囲で指定したもの。
    • 例: image/jpeg;q=0.8, image/png;q=1.0
  • media type list: 許容されるコンテンツタイプをカンマ区切りで列挙したもの。
    • 例: image/jpeg, image/png, text/html

Accept ヘッダーの例

以下の例では、クライアントは JPEG 画像、PNG 画像、HTML テキストのみを受け取ることができ、その中でも PNG 画像を最も優先します。

Accept: image/png;q=1.0, image/jpeg;q=0.8, text/html;q=0.5

コンテンツネゴシエーションの仕組み

  1. クライアントが Accept ヘッダーを含むリクエストを送信します。
  2. サーバーは、リクエストされたリソースに対して複数の表現 (異なる形式のコンテンツ) を持っている場合があることを認識します。
  3. サーバーは、Accept ヘッダーの情報に基づいて、クライアントが処理できる最適な表現を選択します。
  4. サーバーは、選択した表現を Content-Type ヘッダー付きでレスポンスとして返します。

Accept ヘッダーの利点

  • ユーザーエクスペリエンスを向上させることができます。
  • 帯域幅とパフォーマンスを節約できます。
  • クライアントが処理できない形式のコンテンツをサーバーが送信することを防ぎます。
  • クライアントがすべての可能なコンテンツタイプを列挙する必要はありません。
  • Accept ヘッダーは、あくまでもヒントであり、サーバーは必ずしもクライアントの希望に従うとは限りません。


クライアント側コード (Python)

import requests

url = "https://example.com/image.png"

headers = {
    "Accept": "image/png, image/jpeg;q=0.8, */*;q=0.5"
}

response = requests.get(url, headers=headers)

if response.status_code == 200:
    with open("image.png", "wb") as f:
        f.write(response.content)
else:
    print(f"Error: {response.status_code}")

このコードでは、以下の処理が行われます。

  1. image.png という URL に対して GET リクエストを送信します。
  2. サーバーからのレスポンスを受け取ります。
  3. レスポンスのステータスコードが 200 "OK" である場合、レスポンスボディを image.png ファイルとして保存します。
  4. それ以外の場合は、エラーメッセージを出力します。

サーバー側コード (Node.js)

const http = require("http");

const server = http.createServer((req, res) => {
  const url = req.url;
  const acceptHeader = req.headers["accept"];

  if (url === "/image.png") {
    if (acceptHeader.includes("image/png")) {
      // PNG 画像を送信
      res.statusCode = 200;
      res.setHeader("Content-Type", "image/png");
      res.end(readFileSync("image.png"));
    } else if (acceptHeader.includes("image/jpeg")) {
      // JPEG 画像を送信
      res.statusCode = 200;
      res.setHeader("Content-Type", "image/jpeg");
      res.end(readFileSync("image.jpg"));
    } else {
      // 406 Not Acceptable エラーを返す
      res.statusCode = 406;
      res.end();
    }
  } else {
    // 404 Not Found エラーを返す
    res.statusCode = 404;
    res.end();
  }
});

server.listen(3000, () => {
  console.log("Server listening on port 3000");
});
  1. ポート 3000 で HTTP サーバーを起動します。
  2. クライアントからのリクエストを受け取ると、リクエストされた URL と Accept ヘッダーを解析します。
  3. リクエストされた URL が /image.png である場合、以下の処理を行います。
    • Accept ヘッダーに image/png が含まれている場合は、PNG 画像を送信します。
    • Accept ヘッダーに image/jpeg が含まれている場合は、JPEG 画像を送信します。
    • 上記のいずれにも該当しない場合は、406 "Not Acceptable" エラーを返します。
  4. それ以外の URL に対しては、404 "Not Found" エラーを返します。

この例はあくまでも簡易的なものであり、実際のアプリケーションではより複雑なロジックが必要となる場合があります。

  • 言語を指定する: Accept-Language: ja, en-US;q=0.5
  • 特定のエンコーディングを指定する: Accept: text/html; charset=utf-8


Content Negotiation(コンテンツネゴシエーション)を活用する

Content Negotiationは、クライアントとサーバー間でやり取りするコンテンツの形式を最適化する仕組みです。「Accept」ヘッダー以外にも、以下のヘッダーを用いてContent Negotiationをより詳細に制御することができます。

  • Accept-Range: 部分的なレスポンスを受け入れられることを示します。
  • Accept-Language: 許容される言語を指定します。
  • Accept-Encoding: 許容される圧縮方式を指定します。
  • Accept-Charset: 許容される文字エンコーディングを指定します。

これらのヘッダーを組み合わせることで、「Accept」ヘッダーよりもきめ細やかなコンテンツネゴシエーションを実現することができます。

Rangeヘッダーを用いて部分的な取得を行う

すでにご存知のように、Range ヘッダーはファイル全体ではなく、必要な部分のみを要求するために使用できます。これは、特にサイズの大きい画像や動画などのリソースを扱う場合に有効です。

クライアント側で変換を行う

クライアント側で適切な形式に変換できる場合は、「Accept」ヘッダーに頼らず、受信したコンテンツをそのまま処理することもできます。

サーバー側で最適な形式で配信する

クライアントの要求を待たずに、サーバー側で最適な形式でコンテンツを配信することもできます。これは、ユーザーの環境やネットワーク状況を事前に把握できる場合に有効です。

上記以外にも、クライアントとサーバー間でやり取りするコンテンツの形式を制御する方法として、以下の方法が考えられます。

  • ファイルの拡張子: ファイルの拡張子に基づいてコンテンツ形式を判断する。
  • デフォルト形式の利用: サーバー側でデフォルトの形式を定義し、クライアントが何も指定しない場合はその形式で配信する。
  • 明示的な要求: クライアントが、必要なコンテンツ形式を明示的に要求する。

「Accept」ヘッダーの代替手段を選択する際の注意点

上記で紹介した代替手段を選択する際には、以下の点に注意する必要があります。

  • パフォーマンス: 選択した方法が、パフォーマンスに悪影響を与えないことを確認する必要があります。
  • 複雑さの度合い: Content Negotiationなどの仕組みは、「Accept」ヘッダーよりも複雑になる可能性があります。
  • クライアントとサーバーの互換性: 選択した方法が、クライアントとサーバー双方のシステムでサポートされていることを確認する必要があります。