アンチパターン: API プロキシ内で複数値の HTTP ヘッダーに誤った方法でアクセスする

現在、ApigeeApigee ハイブリッドのドキュメントを表示しています。
Apigee Edge のドキュメントはこちらをご覧ください。

HTTP ヘッダーは名前と値のペアで、クライアント アプリケーションとバックエンド サービスはこれを使用して、リクエストとレスポンスそれぞれの追加情報を渡すことができます。以下に簡単な例を示します。

  • Authorization リクエスト ヘッダーは、ユーザーの認証情報をサーバーに渡します。
    Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
  • Content-Type ヘッダーは、送信されるリクエストまたはレスポンスのコンテンツのタイプを示します。
    Content-Type: application/json

HTTP ヘッダーは、ヘッダー フィールドの定義に応じて、1 つまたは複数の値を保持します。1 つのヘッダーが複数の値を保持する場合は、値がカンマで区切られます。次に、複数の値を保持するヘッダーの例を示します。

  • Cache-Control: no-cache, no-store, must-revalidate
  • Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
  • X-Forwarded-For: 10.125.5.30, 10.125.9.125

Apigee では、フロー変数を使用することで、どのポリシーや条件フローからでもヘッダーに簡単にアクセスできます。Apigee で特定のリクエスト ヘッダーまたはレスポンス ヘッダーにアクセスできる変数の一覧を次に示します。

フロー変数:

  • message.header.header-name
  • request.header.header-name
  • response.header.header-name
  • message.header.header-name.N
  • request.header.header-name.N
  • response.header.header-name.N

JavaScript オブジェクト:

  • context.proxyRequest.headers.header-name
  • context.targetRequest.headers.header-name
  • context.proxyResponse.headers.header-name
  • context.targetResponse.headers.header-name

以下は、リクエスト ヘッダーの値を読み取って変数に格納する方法を示す AssignMessage ポリシーのサンプルです。

<AssignMessage continueOnError="false" enabled="true" name="assign-message-default">
  <AssignVariable>
    <Name>reqUserAgent</Name>
    <Ref>request.header.User-Agent</Ref>
  </AssignVariable>
</AssignMessage>

アンチパターン

Apigee ポリシーで HTTP ヘッダーの値にアクセスし、最初の値だけを返す方法は誤りです。特定の HTTP ヘッダーに複数の値がある場合、問題が生じる可能性があります。

以下のセクションでは、ヘッダーにアクセスするいくつかの例を示します。

例 1: JavaScript コードを使用して複数の値を持つ Accept ヘッダーを読み取る

Accept ヘッダーが、次のような複数の値を持つとします。

Accept: text/html, application/xhtml+xml, application/xml

次に、Accept ヘッダーから値を読み取る JavaScript コードの例を示します。

// Read the values from Accept header
var acceptHeaderValues = context.getVariable("request.header.Accept");

この JavaScript コードは、Accept ヘッダーから最初の値だけを返します(この例では text/html)。

例 2: AssignMessage または RaiseFault ポリシーを使用して複数の値を持つ Access-Control-Allow-Headers ヘッダーを読み取る

Access-Control-Allow-Headers ヘッダーが、次のような複数の値を持つとします。

Access-Control-Allow-Headers: content-type, authorization

次のコードは、Access-Control-Allow-Headers ヘッダーを設定する AssignMessage または RaiseFault ポリシーの一部分です。

<Set>
  <Headers>
    <Header name="Access-Control-Allow-Headers">{request.header.Access-Control-Request-Headers}</Header>
  </Headers>
</Set>

このコードでは、リクエスト ヘッダー Access-Control-Allow-Headers からの最初の値(この例では content-type)だけを使って Access-Control-Allow-Headers ヘッダーが設定されます。

影響

  1. 上記のどちらの例でも、複数の値を持つヘッダーから最初の値だけが返されることに注目してください。これらの値がその後、なんらかの関数やロジックを実行する目的で API プロキシフロー内の別のポリシーまたはバックエンド サービスで使用された場合、予期しない結果につながる可能性があります。
  2. リクエスト ヘッダー値がアクセスされて、ターゲット サーバーに渡されると、API リクエストがバックエンドによって不適切に処理され、誤った結果が生じる可能性があります。
  3. クライアント アプリケーションが Apigee レスポンスからの特定のヘッダー値に依存している場合も、不適切に処理され、誤った結果が生じる可能性があります。

ベスト プラクティス

  1. request.header.header_name.values.string 形式のフロー変数を参照して、特定のヘッダーのすべての値を読み取ります。

    例: 複数の値を持つヘッダーを読み取るために RaiseFault または AssignMessage で使用できるサンプル フラグメント

    <Set>
      <Headers>
        <Header name="Inbound-Headers">{request.header.Accept.values.string}</Header>
      </Headers>
    </Set>
  2. 個別の値に個別にアクセスする場合は、適切な組み込みフロー変数(request.header.header_name.values.countrequest.header.header_name.Nresponse.header.header_name.values.countresponse.header.header_name.N)を使用できます。

    次に、JavaScript または Java Callout ポリシー内で処理を繰り返し、特定の 1 つのヘッダーからすべての値を取得します。

    例: 複数の値を持つヘッダーを読み取る JavaScript コードの例

    for (var i = 1; i <=context.getVariable('request.header.Accept.values.count'); i++)
    {
      print(context.getVariable('request.header.Accept.' + i));
    }

    たとえば、application/xml;q=0.9, */*;q=0.8 は、上記のコードでは 2 つの値として表示されます。 1 つ目の値は application/xml;q=0.9、2 つ目の値は */*;q=0.8 です。

    セミコロンを区切り文字として使用してヘッダー値を分割する必要がある場合は、JavaScript コールアウト内で string.split(";") を使用して個別の値を区切ります。

  3. あるいは、フロー変数 request.header.header_name.valuesメッセージ テンプレート内で substring() 関数を使用して、特定のヘッダーのすべての値を読み取ることもできます。

    例: メッセージ テンプレート内で substring() を使用して、複数値のヘッダー全体を読み取る

    <Set>
      <Headers>
       <Header name="Inbound-Headers">{substring(request.header.Accept.values,1,-1)}</Header>
      </Headers>
    </Set>

関連情報