反模式:在 API 代理中错误地访问多值 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

以下是设置 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 设置为请求标头 Access-Control-Allow-Headers 中的第一个值,在此示例中为 content-type

影响

  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 调出政策中的特定标头中提取所有值。

    示例:读取多值标头的示例 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>
    

更多详情