反模式:在 API Proxy 中無法正確存取多值 HTTP 標頭

您正在查看 ApigeeApigee Hybrid 說明文件。
查看 Apigee Edge 說明文件。

HTTP 標頭是名稱值組合,可讓用戶端應用程式和後端服務分別傳遞有關要求和回應的額外資訊。以下是一些簡單的例子:

  • 授權要求標頭會將使用者憑證傳送至伺服器:
    Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
  • Content-Type 標頭會指出傳送的要求/回應內容類型:
    Content-Type: application/json

標頭欄位定義而定,HTTP 標頭可以包含一或多個值。多值標頭的值會以半形逗號分隔。以下列舉幾個包含多個值的標頭範例:

  • 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

以下是 AssignMessage 或 RaiseFault 政策設定 Access-Control-Allow-Headers 標頭的部分程式碼:

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

上述程式碼會設定標頭 Access-Control-Allow-Headers,其中只包含要求標頭 Access-Control-Allow-Headers 的第一個值,在本例中為 content-type

影響

  1. 請注意,在上述兩個範例中,系統只會傳回多值標頭的第一個值。如果這些值之後會由 API Proxy 流程中的其他政策或後端服務用於執行某些函式或邏輯,則可能會導致意外的結果。
  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 或 JavaCallout 政策中的特定標頭擷取所有值。

    範例:讀取多值標頭的 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 會以上述程式碼中的兩個值顯示。第一個值是 application/xml;q=0.9,第二個值是 */*;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>

延伸閱讀