Antipattern: aceda incorretamente a cabeçalhos HTTP de vários valores num proxy de API

Está a ver a documentação do Apigee e do Apigee Hybrid.
Veja a documentação do Apigee Edge.

Os cabeçalhos HTTP são os pares de valores de nomes que permitem que as aplicações cliente e os serviços de back-end transmitam informações adicionais sobre pedidos e respostas, respetivamente. Seguem-se alguns exemplos simples:

  • O cabeçalho do pedido de autorização transmite as credenciais do utilizador ao servidor:
    Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
  • O cabeçalho Content-Type indica o tipo de conteúdo do pedido/resposta que está a ser enviado:
    Content-Type: application/json

Os cabeçalhos HTTP podem ter um ou mais valores, consoante as definições do campo de cabeçalho. Um cabeçalho com vários valores tem valores separados por vírgulas. Seguem-se alguns exemplos de cabeçalhos que contêm vários valores:

  • 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

O Apigee permite que os programadores acedam facilmente aos cabeçalhos através de variáveis de fluxo em qualquer uma das políticas ou fluxos condicionais. Segue-se a lista de variáveis que podem ser usadas para aceder a um cabeçalho de pedido ou de resposta específico no Apigee:

Variáveis de fluxo:

  • 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

Objetos JavaScript:

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

Segue-se um exemplo de uma política AssignMessage que mostra como ler o valor de um cabeçalho de pedido e armazená-lo numa variável:

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

Antipattern

Aceder aos valores dos cabeçalhos HTTP nas políticas do Apigee de uma forma que devolve apenas o primeiro valor é incorreto e pode causar problemas se o cabeçalho HTTP específico tiver mais do que um valor.

As secções seguintes contêm exemplos de acesso ao cabeçalho.

Exemplo 1: leia um cabeçalho Accept com vários valores através de código JavaScript

Considere que o cabeçalho Accept tem vários valores, conforme mostrado abaixo:

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

Segue-se o código JavaScript que lê o valor do cabeçalho Accept:

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

O código JavaScript acima devolve apenas o primeiro valor do cabeçalho Accept, como text/html.

Exemplo 2: ler um cabeçalho Access-Control-Allow-Headers com vários valores na política AssignMessage ou RaiseFault

Considere que o cabeçalho Access-Control-Allow-Headers tem vários valores, conforme mostrado abaixo:

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

Seguem-se as partes do código da política AssignMessage ou RaiseFault que definem o cabeçalho Access-Control-Allow-Headers:

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

O código acima define o cabeçalho Access-Control-Allow-Headers apenas com o primeiro valor do cabeçalho do pedido Access-Control-Allow-Headers, neste exemplo content-type.

Impacto

  1. Em ambos os exemplos acima, repare que apenas é devolvido o primeiro valor dos cabeçalhos com vários valores. Se estes valores forem posteriormente usados por outra política no fluxo do proxy de API ou pelo serviço de back-end para executar alguma função ou lógica, pode levar a um resultado inesperado.
  2. Quando os valores dos cabeçalhos dos pedidos são acedidos e transmitidos para o servidor de destino, os pedidos da API podem ser processados incorretamente pelo back-end e, por conseguinte, podem dar resultados incorretos.
  3. Se a aplicação cliente depender de valores de cabeçalhos específicos da resposta do Apigee, também pode ser processada incorretamente e dar resultados incorretos.

Prática recomendada

  1. Referencie o request.header.header_name.values.string formulário da variável de fluxo para ler todos os valores de um cabeçalho específico.

    Exemplo: fragmento de exemplo que pode ser usado em RaiseFault ou AssignMessage para ler um cabeçalho com vários valores

    <Set>
      <Headers>
        <Header name="Inbound-Headers">{request.header.Accept.values.string}</Header>
      </Headers>
    </Set>
  2. Se quiser acesso individual a cada um dos valores distintos, pode usar as variáveis de fluxo incorporadas adequadas: request.header.header_name.values.count, request.header.header_name.N, response.header.header_name.values.count, response.header.header_name.N.

    Em seguida, itere para obter todos os valores de um cabeçalho específico nas políticas JavaScript ou JavaCallout.

    Exemplo: código JavaScript de exemplo para ler um cabeçalho com vários valores

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

    Por exemplo, application/xml;q=0.9, */*;q=0.8 é apresentado como dois valores com o código acima. O primeiro valor é application/xml;q=0.9 e o segundo é */*;q=0.8 .

    Se os valores do cabeçalho tiverem de ser divididos com um ponto e vírgula como delimitador, pode usar string.split(";") na chamada JavaScript para separar os valores distintos.

  3. Em alternativa, pode usar a função substring() disponível num modelo de mensagem na variável de fluxo request.header.header_name.values para ler todos os valores de um cabeçalho específico.

    Exemplo: use substring() num modelo de mensagem para ler um cabeçalho de vários valores completo

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

Leitura complementar