Antimodèle : accéder de façon incorrecte aux en-têtes HTTP à valeurs multiples dans un proxy d'API

Vous consultez la documentation d'Apigee et d'Apigee hybrid.
Consultez la documentation d'Apigee Edge.

Les en-têtes HTTP sont des paires nom/valeur qui permettent aux applications clientes et aux services de backend de transmettre respectivement des informations supplémentaires sur les requêtes et les réponses. Voici quelques exemples simples :

  • L'en-tête d'une requête d'autorisation transmet les identifiants utilisateur au serveur :
    Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
  • L'en-tête Content-Type indique le type de contenu de la requête/réponse envoyée :
    Content-Type: application/json

Les en-têtes HTTP peuvent être associés à une ou plusieurs valeurs en fonction des définitions des champs d'en-tête. Un en-tête à valeurs multiples contient des valeurs séparées par une virgule. Voici quelques exemples d'en-têtes incluant plusieurs valeurs :

  • 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 permet aux développeurs d'accéder facilement aux en-têtes des règles ou des flux conditionnels à l'aide de variables de flux. Voici la liste des variables permettant d'accéder à un en-tête de requête ou de réponse spécifique dans Apigee :

Variables de flux :

  • 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

Objets JavaScript :

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

Voici un exemple de règle AssignMessage indiquant comment lire la valeur d'un en-tête de requête et la stocker dans une variable :

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

Antimodèle

Il n'est pas indiqué d'accéder aux valeurs des en-têtes HTTP dans les règles Apigee pour renvoyer uniquement la première valeur, car cela peut entraîner des problèmes si l'en-tête HTTP spécifique contient plusieurs valeurs.

Les sections suivantes présentent des exemples d'accès aux en-têtes.

Exemple 1 : Lire un en-tête Accept à valeurs multiples à l'aide du code JavaScript

Notez que l'en-tête Accept contient plusieurs valeurs, comme indiqué ci-dessous :

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

Voici le code JavaScript permettant de lire la valeur de l'en-tête Accept :

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

Le code JavaScript ci-dessus ne renvoie que la première valeur de l'en-tête Accept (par exemple, text/html).

Exemple 2 : Lire un en-tête Access-Control-Allow-Headers à valeurs multiples dans une règle AssignMessage ou RaiseFault

Notez que l'en-tête Access-Control-Allow-Headers contient plusieurs valeurs, comme indiqué ci-dessous :

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

La partie du code de la règle AssignMessage ou RaiseFault permettant de définir l'en-tête Access-Control-Allow-Headers est présentée ci-dessous :

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

Le code ci-dessus ne définit l'en-tête Access-Control-Allow-Headers qu'avec la première valeur de l'en-tête de requête Access-Control-Allow-Headers, content-type dans cet exemple.

Impact

  1. Dans les deux exemples ci-dessus, notez que seule la première valeur des en-têtes à valeurs multiples est renvoyée. Si ces valeurs sont ensuite utilisées par une autre règle du flux de proxy d'API ou par le service de backend pour appliquer une fonction ou une logique, cela peut entraîner un résultat inattendu.
  2. Lorsque les valeurs des en-têtes de requête sont accessibles et transmises au serveur cible, les requêtes API peuvent être traitées par le backend de manière inappropriée et générer des résultats incorrects.
  3. Si l'application cliente dépend des valeurs d'en-tête spécifiques de la réponse Apigee, elle peut également traiter des résultats de manière incorrecte et en générer.

Bonnes pratiques

  1. Référencez la forme request.header.header_name.values.string de la variable de flux pour lire toutes les valeurs d'un en-tête spécifique.

    Exemple : Exemple de fragment pouvant être utilisé dans RaiseFault ou AssignMessage pour lire un en-tête à valeurs multiples

    <Set>
      <Headers>
        <Header name="Inbound-Headers">{request.header.Accept.values.string}</Header>
      </Headers>
    </Set>
  2. Si vous souhaitez accéder individuellement à chacune des valeurs distinctes, vous pouvez utiliser les variables de flux intégrées appropriées : request.header.header_name.values.count, request.header.header_name.N, response.header.header_name.values.count, response.header.header_name.N.

    Effectuez ensuite une itération pour extraire toutes les valeurs d'un en-tête spécifique dans les règles d'appel JavaScript ou Java.

    Exemple : Exemple de code JavaScript permettant de lire un en-tête à valeurs multiples

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

    Par exemple, application/xml;q=0.9, */*;q=0.8 apparaîtra sous la forme de deux valeurs avec le code ci-dessus. La première valeur est application/xml;q=0.9 et la seconde */*;q=0.8.

    Si les valeurs d'en-tête doivent être fractionnées à l'aide d'un point-virgule comme délimiteur, vous pouvez utiliser string.split(";") dans l'appel JavaScript pour séparer les valeurs distinctes.

  3. Vous pouvez également utiliser la fonction substring() disponible dans un modèle de message sur la variable de flux request.header.header_name.values pour lire toutes les valeurs d'un en-tête spécifique.

    Exemple : Utiliser substring() dans un modèle de message pour lire un en-tête à valeurs multiples complet

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

Documentation complémentaire