Guide d'utilisation de la fonction de script d'APICatcher

APICatcher dispose d'un moteur de script basé sur JavaScript intégré, vous permettant d'écrire des scripts personnalisés pour intercepter et modifier les requêtes et les réponses HTTP/HTTPS. La fonction de script est puissante et flexible, adaptée au débogage de développement, aux tests d'API, à la simulation de données et à d'autres scénarios.


Table des matières


Démarrage rapide

  1. Ouvrez APICatcher et accédez à la page de gestion des "Scripts".
  2. Cliquez sur "Ajouter un script", le système remplira automatiquement le code de modèle.
  3. Définissez les conditions de correspondance du script (telles que Host, Path, etc.) pour déterminer quelles requêtes le script affecte.
  4. Écrivez votre logique d'interception.
  5. Après avoir activé le script, commencez la capture de paquets pour qu'elle prenne effet.

Structure de base du script

Chaque script peut contenir deux fonctions :

// Interception de requête (Facultatif)
function interceptRequest(request) {
    // Votre logique
    return { action: 'passthrough' };
}

// Interception de réponse (Facultatif)
function interceptResponse(request, response) {
    // Votre logique
    return { action: 'passthrough' };
}
  • Vous pouvez en définir un seul ou les deux.
  • Les scripts sont exécutés par ordre de priorité et d'heure de création.
  • Une fois qu'un script renvoie un résultat autre que passthrough, les scripts suivants ne seront pas exécutés.

Fonction d'interception de requête interceptRequest

Lorsqu'une requête HTTP correspondante arrive, la fonction interceptRequest est appelée.

Paramètres : objet request

PropriétéTypeDescription
methodstringMéthode HTTP (GET, POST, PUT, DELETE, etc.)
urlstringURL complète
hoststringNom d'hôte
portnumberNuméro de port
pathstringChemin de la requête
schemestringProtocole (http / https)
headersobjectEn-têtes de requête (objet clé-valeur)
bodystringChaîne du corps de la requête
queryParamsobjectParamètres de requête d'URL (objet clé-valeur)

Valeur de retour

Renvoie un objet contenant un champ action pour déterminer comment traiter la requête :

1. Laisser passer la requête (passthrough)

return { action: 'passthrough' };

N'apporte aucune modification, la requête est envoyée normalement.

2. Modifier la requête (modify)

request.headers['Authorization'] = 'Bearer my-token';
request.body = JSON.stringify({ modified: true });
return { action: 'modify', request: request };

Poursuivre l'envoi après avoir modifié le contenu de la requête.

3. Simuler la réponse (mock)

return {
    action: 'mock',
    response: {
        statusCode: 200,
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ success: true, data: 'mocked' })
    }
};

N'envoie pas de véritable requête, renvoie directement la réponse simulée.

4. Rediriger (redirect)

return {
    action: 'redirect',
    url: 'https://new-api.example.com' + request.path,
    statusCode: 302  // Facultatif, 302 par défaut
};

Redirige la requête vers une autre URL.

5. Ignorer la requête (drop)

return { action: 'drop' };

Ignore directement la requête, sans l'envoyer ni y répondre.


Fonction d'interception de réponse interceptResponse

Une fois que le serveur renvoie une réponse, la fonction interceptResponse est appelée.

Paramètres

  • request : Informations sur la requête d'origine (comme ci-dessus)
  • response : Informations sur la réponse
PropriétéTypeDescription
statusCodenumberCode d'état HTTP
headersobjectEn-têtes de réponse (objet clé-valeur)
bodystringChaîne du corps de la réponse

Valeur de retour

1. Laisser passer la réponse (passthrough)

return { action: 'passthrough' };

2. Modifier la réponse (modify)

var data = JSON.parse(response.body);
data.injected = true;
response.body = JSON.stringify(data);
return { action: 'modify', response: response };

3. Retarder la réponse (delay)

return {
    action: 'delay',
    response: response,
    delay: 3000  // Retard de 3000 millisecondes
};

4. Ignorer la réponse (drop)

return { action: 'drop' };

API intégrée

Les objets globaux suivants sont prédéfinis dans l'environnement de script et peuvent être utilisés directement dans interceptRequest et interceptResponse.

httpClient — Requêtes HTTP

Lancer des requêtes HTTP/HTTPS dans le script. L'appel est synchrone, le script attendra que la requête soit terminée avant de poursuivre l'exécution. Le temps passé sur chaque requête sera automatiquement enregistré dans le journal pour faciliter le dépannage des problèmes de performances.

Méthodes

// Requête GET
httpClient.get(url)
httpClient.get(url, headers)

// Requête POST
httpClient.post(url)
httpClient.post(url, body)
httpClient.post(url, body, headers)

// Requête PUT
httpClient.put(url)
httpClient.put(url, body)
httpClient.put(url, body, headers)

// Requête DELETE
httpClient.delete(url)
httpClient.delete(url, headers)

Description des paramètres

ParamètreTypeRequisDescription
urlstringURL de requête complète (http:// ou https://)
bodystringChaîne du corps de la requête (pour JSON, utilisez JSON.stringify())
headersobjectObjet d'en-tête de requête

Valeur de retour

Toutes les méthodes renvoient un objet de réponse de même structure :

{
    statusCode: 200,        // Code d'état HTTP (-1 si la requête échoue)
    headers: { ... },       // Objet d'en-tête de réponse
    body: "...",            // Chaîne du corps de la réponse
    error: ""              // Message d'erreur (chaîne vide en cas de succès)
}

Exemple

// Requête GET simple
var resp = httpClient.get('https://api.example.com/status');
console.log('Status: ' + resp.statusCode);

// Requête GET avec des en-têtes
var resp = httpClient.get('https://api.example.com/user', {
    'Authorization': 'Bearer my-token'
});

// POSTer des données JSON
var resp = httpClient.post(
    'https://api.example.com/data',
    JSON.stringify({ name: 'test', value: 123 }),
    { 'Content-Type': 'application/json' }
);

// Vérifier si la requête a réussi
if (resp.error) {
    console.error('La requête a échoué : ' + resp.error);
} else {
    var data = JSON.parse(resp.body);
    console.log('Données de réponse : ' + JSON.stringify(data));
}

Journalisation automatique du temps consommé

Chaque fois qu'un appel httpClient est terminé, le moteur affiche automatiquement un journal :

[Script] httpClient.get https://api.example.com/status -> 200 (156ms)
[Script] httpClient.post https://api.example.com/data -> 201 (342ms)

Vous pouvez afficher ces informations dans le panneau des journaux d'APICatcher pour vous aider à localiser les requêtes lentes.

⚠️ Attention : Les appels à httpClient sont synchrones et bloqueront l'exécution du script jusqu'à ce que la requête HTTP soit terminée (délai d'attente de 15 secondes). Si vous lancez une requête HTTP qui prend beaucoup de temps dans le script, cela peut retarder la réponse de la requête interceptée d'origine. Veuillez prêter attention aux données de temps consommé dans les journaux.


localStore — Stockage local

Fournit un simple stockage persistant local clé-valeur (Key-Value). Les données sont stockées dans les UserDefaults de l'application, et tous les scripts partagent le même espace de stockage. Si vous devez isoler les données par Host ou d'autres dimensions, veuillez ajouter vous-même un préfixe à la clé (par exemple, request.host + '_token').

Méthodes

// Écrire des données
localStore.write(key, value)

// Lire des données
localStore.read(key)     // Renvoie une chaîne ou null

// Supprimer des données
localStore.remove(key)

Description des paramètres

ParamètreTypeDescription
keystringNom de la clé à stocker
valueanyValeur à stocker (sera automatiquement convertie en chaîne)

Description du stockage

  • Tous les scripts partagent le même espace de stockage
  • Les données sont conservées dans le bac à sable de l'application (stockage local) et existeront toujours après le redémarrage de l'application
  • Si vous avez besoin d'isoler par Host, veuillez inclure les informations Host dans la clé, par exemple :
// Isolation manuelle par host
var key = request.host + '_token';
localStore.write(key, 'my-token');

Exemple

// Lecture et écriture de base
localStore.write('counter', '1');
var count = localStore.read('counter');   // '1'
console.log('Count: ' + count);

// Stocker un objet JSON
var config = { debug: true, maxRetry: 3 };
localStore.write('config', JSON.stringify(config));

// Lire un objet JSON
var raw = localStore.read('config');
if (raw) {
    var config = JSON.parse(raw);
    console.log('Debug mode: ' + config.debug);
}

// Supprimer des données
localStore.remove('counter');
var v = localStore.read('counter');  // null

console — Sortie de journal

Utilisé pour générer des journaux dans le script, qui peuvent être consultés dans le panneau des journaux d'API Catcher.

console.log('Journal normal');          // Niveau d'information
console.warn('Journal d\'avertissement'); // Niveau d'avertissement
console.error('Journal d\'erreur');     // Niveau d'erreur

Prend en charge plusieurs paramètres et les objets seront automatiquement sérialisés en JSON :

console.log('Informations de requête :', request.method, request.url);
console.log('Données de réponse :', { status: response.statusCode, body: response.body });

Exemples pratiques

Exemple 1 : Injection automatique de jeton d'authentification

Interceptez toutes les requêtes vers api.myserver.com et ajoutez automatiquement l'en-tête Authorization. Le Token est obtenu à partir de l'API de connexion et mis en cache dans localStore.

function interceptRequest(request) {
    // Ne traiter que le serveur cible
    if (!request.host.includes('api.myserver.com')) {
        return { action: 'passthrough' };
    }
    
    // Essayer de lire le jeton depuis le cache
    var token = localStore.read('auth_token');
    var tokenExpiry = localStore.read('auth_token_expiry');
    
    // Vérifier si le jeton a expiré (en utilisant l'horodatage, en secondes)
    var now = Math.floor(Date.now() / 1000);
    if (!token || !tokenExpiry || now > parseInt(tokenExpiry)) {
        // Le jeton n'existe pas ou a expiré, reconnectez-vous pour l'obtenir
        console.log('Token expired, re-authenticating...');
        var loginResp = httpClient.post(
            'https://api.myserver.com/auth/login',
            JSON.stringify({ username: 'testuser', password: 'testpass' }),
            { 'Content-Type': 'application/json' }
        );
        
        if (loginResp.statusCode === 200) {
            var loginData = JSON.parse(loginResp.body);
            token = loginData.token;
            // Mettre en cache le jeton, le définir pour qu'il expire dans 1 heure
            localStore.write('auth_token', token);
            localStore.write('auth_token_expiry', String(now + 3600));
            console.log('New token cached successfully');
        } else {
            console.error('Login failed: ' + loginResp.statusCode);
            return { action: 'passthrough' };
        }
    }
    
    // Injecter l'en-tête Authorization
    request.headers['Authorization'] = 'Bearer ' + token;
    return { action: 'modify', request: request };
}

Exemple 2 : Transfert de données d'API/Rapports de journaux

Rapportez les informations de requête et de réponse interceptées à votre serveur d'analyse de journaux.

function interceptResponse(request, response) {
    // Signaler uniquement les API de chemins spécifiques
    if (!request.path.startsWith('/api/v2/')) {
        return { action: 'passthrough' };
    }
    
    // Collecter les informations de requête et de réponse
    var logData = {
        timestamp: new Date().toISOString(),
        request: {
            method: request.method,
            url: request.url,
            headers: request.headers
        },
        response: {
            statusCode: response.statusCode,
            body: response.body
        }
    };
    
    // Signaler au serveur de journaux (remarque : la requête est synchrone et bloquera le retour de la réponse actuelle)
    var reportResp = httpClient.post(
        'https://log.myserver.com/api/collect',
        JSON.stringify(logData),
        { 'Content-Type': 'application/json' }
    );
    
    if (reportResp.error) {
        console.warn('Log report failed: ' + reportResp.error);
    }
    
    // Laisser passer la réponse d'origine
    return { action: 'passthrough' };
}

Exemple 3 : Tests A/B — Modifier les réponses dynamiquement

Utilisez un compteur pour implémenter une simple répartition A/B, en renvoyant alternativement différentes données simulées.

function interceptRequest(request) {
    if (request.path !== '/api/recommend') {
        return { action: 'passthrough' };
    }
    
    // Lire le compteur alternatif à partir de localStore
    var count = parseInt(localStore.read('ab_counter') || '0');
    count++;
    localStore.write('ab_counter', String(count));
    
    // Renvoyer alternativement différentes versions pour les nombres pairs et impairs
    var version = (count % 2 === 0) ? 'A' : 'B';
    
    if (version === 'A') {
        return {
            action: 'mock',
            response: {
                statusCode: 200,
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    version: 'A',
                    items: [{ id: 1, name: 'Plan de recommandation A-1' }, { id: 2, name: 'Plan de recommandation A-2' }]
                })
            }
        };
    } else {
        return {
            action: 'mock',
            response: {
                statusCode: 200,
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    version: 'B',
                    items: [{ id: 3, name: 'Plan de recommandation B-1' }, { id: 4, name: 'Plan de recommandation B-2' }]
                })
            }
        };
    }
}

Exemple 4 : Protection de limite de débit de requêtes

Enregistrez la fréquence de requête de la même API et renvoyez l'erreur 429 lorsqu'elle dépasse le seuil.

function interceptRequest(request) {
    if (!request.path.startsWith('/api/')) {
        return { action: 'passthrough' };
    }
    
    var key = 'rate_' + request.path;
    var now = Math.floor(Date.now() / 1000);
    
    // Lire l'heure et le nombre de la dernière requête
    var rawData = localStore.read(key);
    var data = rawData ? JSON.parse(rawData) : { count: 0, windowStart: now };
    
    // Si la fenêtre de temps dépasse 60 secondes, réinitialiser le compteur
    if (now - data.windowStart > 60) {
        data = { count: 0, windowStart: now };
    }
    
    data.count++;
    localStore.write(key, JSON.stringify(data));
    
    // Maximum de 10 fois par minute
    if (data.count > 10) {
        console.warn('Rate limit exceeded for ' + request.path);
        return {
            action: 'mock',
            response: {
                statusCode: 429,
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    error: 'Too Many Requests',
                    retryAfter: 60 - (now - data.windowStart)
                })
            }
        };
    }
    
    return { action: 'passthrough' };
}

Remarques

  1. httpClient est un appel synchrone : Les requêtes HTTP lancées dans le script bloqueront l'exécution du script jusqu'à ce que la requête soit terminée ou expire (15 secondes). Cela affectera la vitesse de traitement de la requête d'origine. Veuillez faire attention aux informations sur le temps de consommation de la requête qui sont automatiquement générées dans les journaux.

  2. Stockage partagé localStore : Tous les scripts partagent le même espace de stockage. Si vous avez besoin d'isoler des données par Host, veuillez ajouter vous-même un préfixe à la clé.

  3. Ordre d'exécution du script : Plusieurs scripts sont triés et exécutés par priorité. Une fois qu'un script renvoie un résultat différent de passthrough, les scripts suivants seront ignorés.

  4. Scripts distants : Prend en charge le chargement de scripts à partir d'URL. Le contenu du script distant sera automatiquement extrait lors du démarrage de la capture de paquets. Si l'extraction échoue, le script sera ignoré.

  5. Traitement JSON : Le corps des requêtes/réponses est composé de chaînes. Lorsque vous devez traiter des données JSON, utilisez JSON.parse() pour les analyser et JSON.stringify() pour les sérialiser.

  6. Gestion des erreurs : Il est recommandé d'utiliser try-catch dans la logique clé et d'enregistrer les exceptions via console.error() pour éviter que la chaîne d'interception ne soit interrompue en raison d'un plantage du script.

  7. Fonctions auxiliaires : Le moteur possède deux fonctions utilitaires intégrées :

    • safeJsonParse(str) — Analyse JSON sécurisée, renvoie null en cas d'échec
    • deepClone(obj) — Copie approfondie de l'objet