Guía de uso de la función de script de APICatcher

APICatcher tiene un motor de secuencias de comandos basado en JavaScript integrado, lo que le permite escribir secuencias de comandos personalizadas para interceptar y modificar solicitudes y respuestas HTTP/HTTPS. La función de secuencias de comandos es potente y flexible, adecuada para la depuración de desarrollo, pruebas de API, simulación de datos y otros escenarios.


Índice


Inicio rápido

  1. Abra APICatcher y vaya a la página de administración de "Scripts".
  2. Haga clic en "Agregar script", y el sistema completará automáticamente el código de plantilla.
  3. Establezca las Condiciones de coincidencia del script (como Host, Path, etc.) para determinar a qué solicitudes afecta el script.
  4. Escriba su lógica de intercepción.
  5. Después de habilitar el script, comience a capturar paquetes para que surta efecto.

Estructura básica del script

Cada script puede contener dos funciones:

// Intercepción de solicitud (Opcional)
function interceptRequest(request) {
    // Tu lógica
    return { action: 'passthrough' };
}

// Intercepción de respuesta (Opcional)
function interceptResponse(request, response) {
    // Tu lógica
    return { action: 'passthrough' };
}
  • Puedes definir solo uno de ellos, o definir ambos.
  • Los scripts se ejecutan en orden de prioridad y tiempo de creación.
  • Una vez que un script devuelve un resultado distinto de passthrough, los scripts posteriores no se ejecutarán.

Función de intercepción de solicitud interceptRequest

Cuando llega una solicitud HTTP que coincide, se llama a la función interceptRequest.

Parámetros: objeto request

PropiedadTipoDescripción
methodstringMétodo HTTP (GET, POST, PUT, DELETE, etc.)
urlstringURL completa
hoststringNombre de host
portnumberNúmero de puerto
pathstringRuta de la solicitud
schemestringProtocolo (http / https)
headersobjectEncabezados de solicitud (objeto clave-valor)
bodystringCadena del cuerpo de la solicitud
queryParamsobjectParámetros de consulta de URL (objeto clave-valor)

Valor de retorno

Devuelve un objeto que contiene un campo action para determinar cómo procesar la solicitud:

1. Permitir el paso de la solicitud (passthrough)

return { action: 'passthrough' };

No hace ninguna modificación, la solicitud se envía normalmente.

2. Modificar la solicitud (modify)

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

Continúa enviando después de modificar el contenido de la solicitud.

3. Simular respuesta (mock)

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

No envía la solicitud real, devuelve directamente la respuesta simulada.

4. Redireccionar (redirect)

return {
    action: 'redirect',
    url: 'https://new-api.example.com' + request.path,
    statusCode: 302  // Opcional, predeterminado 302
};

Redirecciona la solicitud a otra URL.

5. Descartar la solicitud (drop)

return { action: 'drop' };

Descarta la solicitud directamente, sin enviarla ni responderla.


Función de intercepción de respuesta interceptResponse

Después de que el servidor devuelve una respuesta, se llama a la función interceptResponse.

Parámetros

  • request: Información de la solicitud original (igual que arriba)
  • response: Información de la respuesta
PropiedadTipoDescripción
statusCodenumberCódigo de estado HTTP
headersobjectEncabezados de respuesta (objeto clave-valor)
bodystringCadena del cuerpo de la respuesta

Valor de retorno

1. Permitir el paso de la respuesta (passthrough)

return { action: 'passthrough' };

2. Modificar la respuesta (modify)

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

3. Retrasar la respuesta (delay)

return {
    action: 'delay',
    response: response,
    delay: 3000  // Retraso de 3000 milisegundos
};

4. Descartar la respuesta (drop)

return { action: 'drop' };

API integrada

Los siguientes objetos globales están preestablecidos en el entorno del script y se pueden usar directamente en interceptRequest y interceptResponse.

httpClient — Solicitudes HTTP

Inicia solicitudes HTTP/HTTPS dentro del script. La llamada es síncrona, el script esperará a que se complete la solicitud antes de continuar la ejecución. El tiempo que tarda cada solicitud se registrará automáticamente en el registro para facilitar la resolución de problemas de rendimiento.

Métodos

// Solicitud GET
httpClient.get(url)
httpClient.get(url, headers)

// Solicitud POST
httpClient.post(url)
httpClient.post(url, body)
httpClient.post(url, body, headers)

// Solicitud PUT
httpClient.put(url)
httpClient.put(url, body)
httpClient.put(url, body, headers)

// Solicitud DELETE
httpClient.delete(url)
httpClient.delete(url, headers)

Descripción de parámetros

ParámetroTipoRequeridoDescripción
urlstringURL de solicitud completa (http:// o https://)
bodystringCadena del cuerpo de la solicitud (para JSON, use JSON.stringify())
headersobjectObjeto de encabezado de solicitud

Valor de retorno

Todos los métodos devuelven un objeto de respuesta de la misma estructura:

{
    statusCode: 200,        // Código de estado HTTP (-1 si la solicitud falla)
    headers: { ... },       // Objeto de encabezado de respuesta
    body: "...",            // Cadena del cuerpo de la respuesta
    error: ""              // Mensaje de error (cadena vacía si tiene éxito)
}

Ejemplo

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

// Solicitud GET con encabezados
var resp = httpClient.get('https://api.example.com/user', {
    'Authorization': 'Bearer my-token'
});

// POST de datos JSON
var resp = httpClient.post(
    'https://api.example.com/data',
    JSON.stringify({ name: 'test', value: 123 }),
    { 'Content-Type': 'application/json' }
);

// Comprobar si la solicitud se realizó con éxito
if (resp.error) {
    console.error('La solicitud falló: ' + resp.error);
} else {
    var data = JSON.parse(resp.body);
    console.log('Datos de respuesta: ' + JSON.stringify(data));
}

Registro automático del tiempo de consumo

Cada vez que se completa una llamada httpClient, el motor emitirá automáticamente un registro:

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

Puede ver esta información en el panel de registro de APICatcher para ayudar a localizar solicitudes lentas.

⚠️ Nota: Las llamadas a httpClient son síncronas y bloquearán la ejecución del script hasta que se complete la solicitud HTTP (tiempo de espera de 15 segundos). Si inicia una solicitud HTTP que consume mucho tiempo en el script, puede causar un retraso en la respuesta de la solicitud interceptada original. Preste atención a los datos de tiempo de consumo en los registros.


localStore — Almacenamiento local

Proporciona almacenamiento persistente local simple de clave-valor (Key-Value). Los datos se almacenan en UserDefaults de la aplicación y todos los scripts comparten el mismo espacio de almacenamiento. Si necesita aislar datos por Host u otras dimensiones, agregue un prefijo a la clave usted mismo (por ejemplo, request.host + '_token').

Métodos

// Escribir datos
localStore.write(key, value)

// Leer datos
localStore.read(key)     // Devuelve string o null

// Eliminar datos
localStore.remove(key)

Descripción de parámetros

ParámetroTipoDescripción
keystringEl nombre de clave a almacenar
valueanyEl valor a almacenar (se convertirá automáticamente a cadena)

Descripción del almacenamiento

  • Todos los scripts comparten el mismo espacio de almacenamiento
  • Los datos se conservan en la zona de pruebas de la aplicación (almacenamiento local) y siguen existiendo después de reiniciar la aplicación
  • Si necesita aislar por Host, incluya información del Host en la clave, por ejemplo:
// Aislamiento manual por host
var key = request.host + '_token';
localStore.write(key, 'my-token');

Ejemplo

// Lectura y escritura básicas
localStore.write('counter', '1');
var count = localStore.read('counter');   // '1'
console.log('Count: ' + count);

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

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

// Eliminar datos
localStore.remove('counter');
var v = localStore.read('counter');  // null

console — Salida de registro

Se utiliza para enviar registros dentro del script, que se pueden ver en el panel de registros de API Catcher.

console.log('Registro normal');          // Nivel de información
console.warn('Registro de advertencia'); // Nivel de advertencia
console.error('Registro de error');      // Nivel de error

Admite múltiples parámetros y los objetos se serializarán automáticamente en JSON:

console.log('Información de la solicitud:', request.method, request.url);
console.log('Datos de respuesta:', { status: response.statusCode, body: response.body });

Ejemplos prácticos

Ejemplo 1: Inyección automática de token de autenticación

Intercepte todas las solicitudes a api.myserver.com y agregue automáticamente el encabezado Authorization. El Token se obtiene de la API de inicio de sesión y se almacena en caché en localStore.

function interceptRequest(request) {
    // Solo procesar el servidor de destino
    if (!request.host.includes('api.myserver.com')) {
        return { action: 'passthrough' };
    }
    
    // Intentar leer el token del caché
    var token = localStore.read('auth_token');
    var tokenExpiry = localStore.read('auth_token_expiry');
    
    // Comprobar si el token ha caducado (usando la marca de tiempo, en segundos)
    var now = Math.floor(Date.now() / 1000);
    if (!token || !tokenExpiry || now > parseInt(tokenExpiry)) {
        // El token no existe o ha caducado, vuelva a iniciar sesión para obtenerlo
        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;
            // Caché del token, configúrelo para que caduque en 1 hora
            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' };
        }
    }
    
    // Inyectar encabezado Authorization
    request.headers['Authorization'] = 'Bearer ' + token;
    return { action: 'modify', request: request };
}

Ejemplo 2: Reenvío de datos de API/Informes de registro

Informe de la información de solicitud y respuesta interceptada a su servidor de análisis de registros.

function interceptResponse(request, response) {
    // Solo informar API de rutas específicas
    if (!request.path.startsWith('/api/v2/')) {
        return { action: 'passthrough' };
    }
    
    // Recopilar información de solicitud y respuesta
    var logData = {
        timestamp: new Date().toISOString(),
        request: {
            method: request.method,
            url: request.url,
            headers: request.headers
        },
        response: {
            statusCode: response.statusCode,
            body: response.body
        }
    };
    
    // Informar al servidor de registro (nota: la solicitud es síncrona y bloqueará el retorno de la respuesta actual)
    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);
    }
    
    // Permitir pasar la respuesta original
    return { action: 'passthrough' };
}

Ejemplo 3: Pruebas A/B — Modificar respuestas dinámicamente

Utilice un contador para implementar una división A/B simple, devolviendo alternativamente diferentes datos simulados.

function interceptRequest(request) {
    if (request.path !== '/api/recommend') {
        return { action: 'passthrough' };
    }
    
    // Leer el contador alternativo de localStore
    var count = parseInt(localStore.read('ab_counter') || '0');
    count++;
    localStore.write('ab_counter', String(count));
    
    // Devolver alternativamente diferentes versiones
    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 recomendación A-1' }, { id: 2, name: 'Plan de recomendación 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 recomendación B-1' }, { id: 4, name: 'Plan de recomendación B-2' }]
                })
            }
        };
    }
}

Ejemplo 4: Protección de límite de velocidad de solicitud

Registre la frecuencia de solicitud de la misma API y devuelva el error 429 cuando supere el umbral.

function interceptRequest(request) {
    if (!request.path.startsWith('/api/')) {
        return { action: 'passthrough' };
    }
    
    var key = 'rate_' + request.path;
    var now = Math.floor(Date.now() / 1000);
    
    // Leer el tiempo y el conteo de la última solicitud
    var rawData = localStore.read(key);
    var data = rawData ? JSON.parse(rawData) : { count: 0, windowStart: now };
    
    // Si la ventana de tiempo supera los 60 segundos, restablezca el contador
    if (now - data.windowStart > 60) {
        data = { count: 0, windowStart: now };
    }
    
    data.count++;
    localStore.write(key, JSON.stringify(data));
    
    // Máximo 10 veces por minuto
    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' };
}

Notas

  1. httpClient es una llamada síncrona: Las solicitudes HTTP iniciadas en el script bloquearán la ejecución del script hasta que se complete la solicitud o se agote el tiempo de espera (15 segundos). Esto afectará la velocidad de procesamiento de la solicitud original. Preste atención a la información del tiempo de consumo de la solicitud que se genera automáticamente en los registros.

  2. Almacenamiento compartido localStore: Todos los scripts comparten el mismo espacio de almacenamiento. Si necesita aislar datos por Host, agregue un prefijo en la clave usted mismo.

  3. Orden de ejecución del script: Varios scripts se ordenan y ejecutan por prioridad. Una vez que un script devuelve un resultado distinto de passthrough, los scripts posteriores se omitirán.

  4. Scripts remotos: Soporta la carga de scripts desde URL. El contenido del script remoto se extraerá automáticamente al iniciar la captura de paquetes. Si la extracción falla, se omitirá el script.

  5. Procesamiento de JSON: El cuerpo de las solicitudes/respuestas son cadenas. Cuando necesite procesar datos JSON, utilice JSON.parse() para analizar y JSON.stringify() para serializar.

  6. Manejo de errores: Se recomienda utilizar try-catch en la lógica clave y registrar las excepciones mediante console.error() para evitar que la cadena de intercepción se interrumpa debido a un bloqueo del script.

  7. Funciones auxiliares: El motor tiene dos funciones de utilidad integradas:

    • safeJsonParse(str) — Análisis seguro de JSON, devuelve un valor nulo en caso de error
    • deepClone(obj) — Copia profunda de objetos