反模式:在 API 代理中错误地访问多值 HTTP 标头

您正在查看的是 Apigee Edge 文档。
转到 Apigee X 文档
信息

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 Edge 允许开发者使用任何 Edge 政策或条件流中的流变量轻松访问标头。下面列出了可用于在 Edge 中访问特定请求或响应标头的变量:

流变量:

  • 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>

反模式

在 Edge 政策中以仅返回第一个值的方式访问 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. 如果客户端应用依赖于 Edge 响应中的特定标头值,则它可能也会错误地处理并给出错误的结果。

最佳做法

  1. 使用相应的内置流变量: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 将与上述代码一起显示为一个值。

    如果需要使用分号作为分隔符拆分标头值,请使用 string.split(";") 将这些值分隔为多个值。

  2. 在 RaiseFault 或 AssignMessage 政策中的流变量 request.header.header_name.values 上使用 substring() 函数可读取特定标头的所有值。

    示例:读取多值标头的 RaiseFault 或 AssignMessage 政策

    <Set>
      <Headers>
       <Header name="Access-Control-Allow-Headers">{substring(request.header.Access-Control-Request-Headers.values,1,-1)}</Header>
      </Headers>
    </Set>
    

深入阅读