Exploiter JSONP pour surmonter les restrictions CORS

La Politique de Même Origine

La politique de même origine est un mécanisme de sécurité fondamental dans les navigateurs web. Elle stipule qu'un script exécuté dans une page web ne peut accéder qu'aux ressources provenant de la même origine que celle de la page elle-même. Une origine est définie par la combinaison du protocole (HTTP/HTTPS), du nom de domaine et du port.

Lorsque des requêtes AJAX tentent de contourner cette politique, le navigateur bloque l'opération, générant généralement une erreur du type "Accès refusé" ou "Permission denied". Pour résoudre ce problème de communication inter-domaines (cross-domain), une technique appelée JSONP est couramment employée.

JSON et JSONP : Une Distinction Clé

Il est essentiel de différencier JSONP de JSON.

JSON (JavaScript Object Notation) est un format léger d'échange de données, facile à lire et à écrire pour les humains, et facile à analyser et à générer pour les machines. Il est largement utilisé dans les applications web.

JSONP (JSON with Padding), quant à lui, n'est pas une fonctionnalité native de JSON mais un protocole de communication non officiel. Il exploite la manière dont les balises <script> peuvent charger des ressources depuis n'importe quel domaine, indépendamment de la politique de même origine. JSONP permet à un serveur de renvoyer des données dans un format qui est, en quelque sorte, "rembourré" (padded) avec une fonction de rappel JavaScript. Cette fonction est ensuite exécutée par le navigateur, permettant ainsi de traiter les données reçues.

Principe de Fonctionnement du JSONP

Le cœur du mécanisme JSONP réside dans l'utilisation de la balise <script>. Contrairement aux requêtes AJAX classiques, le chargement d'un script via la balise <script> n'est pas soumis aux restrictions de la politique de même origine.

Considérons un scénario simple :

  • Page cliente (domaine A) : Contient une fonction de rappel JavaScript définie.
  • Serveur distant (domaine B) : Une URL qui, lorsqu'elle est appelée, renvoie un script JavaScript qui exécute la fonction de rappel définie par le client, en lui passant des données au format JSON comme argument.

Le navigateur charge la balise <script> dont l'attribut src pointe vers l'URL du domaine B. Le script renvoyé par le domaine B, par exemple, pourrait ressembler à ceci : callbackFunction({"clé": "valeur"});. Le navigateur, en exécutant ce script, appelle la fonction callbackFunction définie dans le domaine A, lui transmettant l'objet JSON {"clé": "valeur"}.

Implémentation : La fonction de Rappel (Callback)

Illustrons cela avec un exemple concret. Supposons que notre page cliente (index.html) souhaite obtenir des données d'un serveur distant (api.js).

index.html (Côté client) :


<script type="text/javascript">
    // Définition de la fonction de rappel
    function traiterResultats(donnees) {
        console.log("Données reçues :", donnees.message);
    }
</script>
<script type="text/javascript" src="http://serveur-distant.com/api.js"></script>

api.js (Côté serveur distant) :


// Appel de la fonction de rappel définie par le client avec des données JSON
traiterResultats({message: "Succès de la requête inter-domaines !"});

Lorsque index.html est chargé, le navigateur tente de charger http://serveur-distant.com/api.js. Le script renvoyé par le serveur exécute alors traiterResultats, qui est définie dans index.html, affichant le message.

Chargement Dynamique des Scripts

Pour plus de flexibilité, la création de la balise <script> peut être effectuée dynamiquement via JavaScript.


function ajouterScript(urlSource) {
    var elementScript = document.createElement('script');
    elementScript.setAttribute("type", "text/javascript");
    elementScript.src = urlSource;
    document.body.appendChild(elementScript);
}

window.onload = function() {
    // Appel pour charger le script distant
    ajouterScript("http://serveur-distant.com/api.js");
};

Cette approche permet de déclencher la requête JSONP à un moment précis, par exemple, après le chargement complet de la page.

JSONP avec des Services Réels

De nombreux services web exposent des API compatibles JSONP. L'un des paramètres clés est souvent un paramètre de requête qui spécifie le nom de la fonction de rappel. Par exemple, une requête vers un service Google pourrait ressembler à : ...&callback=nomFonctionClient.


<script type="text/javascript">
    function maFonctionCallback(resultats) {
        console.log("Première URL trouvée :", resultats.responseData.results[0].unescapedUrl);
    }

    function chargerDonneesApi(urlApi) {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        // Envoi du nom de la fonction de rappel au serveur
        script.src = urlApi + '&callback=maFonctionCallback';
        document.body.appendChild(script);
    }

    window.onload = function() {
        // Exemple avec une API de recherche (URL fictive pour l'illustration)
        chargerDonneesApi("http://api.exemple.com/recherche?terme=donnees");
    };
</script>

Le serveur distant, en recevant une requête avec callback=maFonctionCallback, renverra une réponse comme : maFonctionCallback({"responseData": ...});.

Créer son Propre Service JSONP

Pour mettre en place un service JSONP simple, le serveur doit écouter le paramètre callback dans la requête et formater la réponse en conséquence.

Exemple côté serveur (PHP) :


<?php
    $callback = isset($_GET['callback']) ? $_GET['callback'] : 'callback'; // Nom de la fonction par défaut
    $donnees = array(
        "utilisateur" => "Alice",
        "statut" => "actif"
    );
    header('Content-Type: application/javascript');
    echo $callback . '(' . json_encode($donnees) . ');';
?>

Exemple côté client (JavaScript) :


<script type="text/javascript">
    function afficherInfosUtilisateur(data) {
        console.log("Utilisateur :", data.utilisateur, "Statut :", data.statut);
    }

    // Appel du service PHP
    var urlService = "http://mon-serveur.com/mon_service.php?callback=afficherInfosUtilisateur";
    var script = document.createElement('script');
    script.src = urlService;
    document.body.appendChild(script);
</script>

JSONP avec jQuery

jQuery simplifie grandement l'utilisation de JSONP.

Utilisation de $.getJSON()

La méthode $.getJSON() peut être utilisée pour des requêtes JSONP. Il suffit de spécifier un callback=? dans l'URL.


$.getJSON("http://mon-serveur.com/mon_service.php?callback=?", function(data) {
    console.log("Utilisateur (jQuery) :", data.utilisateur, "Statut :", data.statut);
});

jQuery générera automatiquement un nom de fonction de rappel unique et mettra à jour l'URL du service avec ce nom. Le paramètre callback=? indique à jQuery d'utiliser le mécanisme JSONP.

Utilisation de $.ajax() pour plus de contrôle

Pour un contrôle plus fin, notamment pour spécifier le nom du paramètre de rappel ou utiliser un nom de fonction de rappel personnalisé, la méthode $.ajax() est plus appropriée.


$.ajax({
    url: "http://mon-serveur.com/mon_service.php",
    dataType: "jsonp", // Indique à jQuery d'utiliser JSONP
    jsonp: "callback", // Nom du paramètre de rappel à envoyer au serveur
    jsonpCallback: "traitementSpecifique", // Nom de la fonction de rappel personnalisée
    success: function(data) {
        console.log("Utilisateur (AJAX) :", data.utilisateur, "Statut :", data.statut);
    }
});

Dans cet exemple, jQuery enverra la requête à http://mon-serveur.com/mon_service.php?callback=traitementSpecifique. Le serveur devra alors renvoyer une réponse comme traitementSpecifique({...});.

JSONP est une solution efficace pour contourner les restrictions de sécurité liées aux requêtes inter-domaines, bien qu'il soit important de noter ses limitations (par exemple, il ne supporte que les requêtes GET et peut présenter des risques de sécurité si les données ne sont pas correctement validées).

Étiquettes: JSONP CORS JavaScript AJAX API

Publié le 18 juin à 16h54