Ajout de la compatibilité du CORS à un proxy d'API

Vous consultez la documentation d'Apigee Edge.
Consultez la documentation Apigee X.
en savoir plus

CORS (Cross-Origin Resource Sharing) est un mécanisme standard qui permet aux appels XMLHttpRequest (XHR) exécutés sur une page Web d'interagir avec des ressources extérieures au domaine d'origine. CORS est une solution couramment mise en œuvre en réponse à la "règle de même origine" appliquée par tous les navigateurs. Par exemple, si vous effectuez un appel XHR à l'API Twitter à partir de code JavaScript exécuté dans votre navigateur, l'appel échoue. En effet, le domaine qui diffuse la page vers votre navigateur est différent du domaine diffusant l'API Twitter. CORS offre une solution à ce problème en autorisant des serveurs à "accepter" explicitement le partage de ressources entre origines multiples.

Vidéo : Regardez une courte vidéo pour savoir comment activer CORS sur un proxy d'API.

Cas d'utilisation type pour le CORS

Le code JQuery suivant appelle un service cible fictif. S'il est exécuté depuis le contexte d'un navigateur (une page Web), l'appel échoue en raison de la règle de même origine :

<script>
var url = "http://service.example.com";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This is where we end up!
            }
    });
  });
});
</script>

Une solution à ce problème consiste à créer un proxy d'API Apigee qui appelle l'API de service sur le backend. N'oubliez pas qu'Edge se trouve entre le client (un navigateur, dans ce cas) et l'API backend (le service). Étant donné que le proxy d'API s'exécute sur le serveur, et non dans le navigateur, il est en mesure d'appeler le service. Ensuite, il vous suffit de joindre des en-têtes CORS à la réponse TargetEndpoint. Du moment que le navigateur est compatible avec CORS, ces en-têtes indiquent au navigateur qu'il est autorisé à "assouplir" sa règle de même origine, permettant ainsi à l'appel d'API inter-origine de réussir.

Une fois le proxy compatible avec le CORS créé, vous pouvez appeler l'URL du proxy d'API au lieu du service de backend dans votre code côté client. Exemple :

<script>
var url = "http://myorg-test.apigee.net/v1/example";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This time, we do not end up here!
            }
    });
  });
});
</script>

Associer une règle CORS à un nouveau proxy d'API

Vous pouvez ajouter la compatibilité CORS à un proxy d'API en attribuant une stratégie d'ajout de CORS au proxy d'API lors de sa création. Pour ajouter cette stratégie, cochez la case Ajouter des en-têtes CORS sur la page Sécurité de l'assistant Créer un proxy.

Lorsque vous cochez cette case, une stratégie appelée "Ajouter le CORS" est automatiquement ajoutée au système et associée au préflux de réponse TargetEndpoint, comme illustré dans la figure suivante :

Stratégie d&#39;ajout de CORS ajoutée au navigateur sous Stratégies et associée au flux de réponse TargetEndpoint dans le volet de droite

La règle "Add CORS" (Ajouter une règle CORS) est mise en œuvre en tant que règleAssignMessage, qui ajoute les en-têtes appropriés à la réponse. En réalité, ces en-têtes indiquent au navigateur les origines avec lesquelles il partage ses ressources, les méthodes qu'il accepte, etc. Pour en savoir plus sur ces en-têtes CORS, consultez les recommandations W3C Cross-Origin Resource Sharing.

Vous devez modifier la stratégie comme suit:

  • Ajoutez les en-têtes content-type et authorization (nécessaires pour l'authentification de base ou OAuth2) à l'en-tête Access-Control-Allow-Headers, comme indiqué dans l'extrait de code ci-dessous.
  • Pour l'authentification OAuth2, vous devrez peut-être prendre des mesures pour corriger le comportement non conforme au document RFC.
  • Il est recommandé d'utiliser <Set> au lieu de <Add> pour définir les en-têtes CORS, comme illustré dans l'extrait de code ci-dessous. Si vous utilisez <Add> et que l'en-tête Access-Control-Allow-Origin existe déjà, vous recevrez l'erreur suivante :

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

    Pour en savoir plus, consultez la section Erreur CORS : l'en-tête contient plusieurs valeurs "*, *", mais une seule est autorisée.

<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Ajouter des en-têtes CORS à un proxy existant

Vous devez créer manuellement une nouvelle règle d'attribution de message et y copier le code de la règle "Add CORS" figurant dans la section précédente. Associez ensuite la règle au PreFlow de réponse du TargetEndpoint du proxy d'API. Vous pouvez modifier les valeurs d'en-tête si nécessaire. Pour plus d'informations sur la création et l'association de règles, consultez la section Qu'est-ce qu'une règle ?

Gérer les requêtes CORS de vérification préliminaire

La vérification préliminaire CORS consiste à envoyer une requête à un serveur pour vérifier s'il est compatible avec CORS. Les réponses de vérification préliminaire types incluent les origines à partir desquelles le serveur acceptera les requêtes CORS, une liste de méthodes HTTP compatibles avec les requêtes CORS, les en-têtes pouvant être utilisés dans le cadre de la requête de ressource, la durée maximale de la réponse de vérification préliminaire qui sera mise en cache, etc. Si le service n'indique pas la compatibilité CORS ou ne souhaite pas accepter les requêtes à origines multiples provenant de l'origine du client, la stratégie à origines multiples du navigateur est appliquée et toute requête interdomaine effectuée à partir du client pour interagir avec des ressources hébergées sur ce serveur échouera.

En règle générale, les requêtes CORS de vérification préliminaire sont effectuées avec la méthode HTTP OPTIONS. Lorsqu'un serveur compatible avec CORS reçoit une requête OPTIONS, il renvoie au client un ensemble d'en-têtes CORS indiquant son niveau de compatibilité CORS. Grâce à ce handshake, le client sait ce qu'il est autorisé à demander à partir du domaine d'origine extérieure.

Pour en savoir plus sur la vérification préliminaire, consultez la page Recommandation W3C Cross-Origin Resource Sharing. Vous y trouverez de nombreux articles et blogs sur le CORS.

Apigee n'inclut pas de solution de vérification CORS préliminaire prête à l'emploi, mais il est possible de mettre en œuvre une telle solution, comme décrit dans cette section. L'objectif est que le proxy puisse évaluer une requête OPTIONS dans un flux conditionnel. Le proxy peut alors renvoyer au client une réponse appropriée.

Examinons un exemple de flux, puis discutons des parties qui gèrent la requête préliminaire:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request/>
            <Response>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Response>
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
        </Flow>
    </Flows>

    <PreFlow name="PreFlow">
        <Request/>
        <Response/>

    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

Les parties clés de ce ProxyEndpoint sont les suivantes :

  • Une règle RouteRule est créée sur une cible NULL avec une condition pour la requête OPTIONS. Notez qu'aucun TargetEndpoint n'est spécifié. Si une requête OPTIONS est reçue et que les en-têtes de requête Origin et Access-Control-Request-Method sont non nuls, le proxy renvoie immédiatement les en-têtes CORS dans une réponse au client (en contournant la cible "backend" par défaut actuelle). Pour en savoir plus sur les conditions de flux et la règle RouteRule, consultez la section Conditions avec variables de flux.

    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    
  • Un flux OptionsPreFlight est créé. Il ajoute une règle CORS (Add CORS) (contenant les en-têtes CORS) au flux si une requête OPTIONS est reçue et si les en-têtes de requête Origin et Access-Control-Request-Method ne sont pas nuls.

     <Flow name="OptionsPreFlight">
                <Request/>
                <Response>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Response>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>
    

Utiliser l'exemple de solution CORS

Un exemple de solution CORS, mis en œuvre en tant que flux partagé, est disponible sur GitHub. Importez dans votre environnement le fichier de groupe de flux partagé et associez-le à l'aide de hooks de flux ou directement dans les flux du proxy d'API. Pour plus d'informations, consultez le fichier README CORS-Shared-FLow fourni avec l'exemple.