APICatcher 指令碼功能使用指南
APICatcher 內建了基於 JavaScript 的指令碼引擎,允許你撰寫自訂指令碼來攔截和修改 HTTP/HTTPS 請求與回應。指令碼功能強大且靈活,適合開發除錯、介面測試、資料模擬等場景。
目錄
快速開始
- 打開 APICatcher,進入「指令碼」管理頁面
- 點擊「新增指令碼」,系統會自動填充模板程式碼
- 設定指令碼的 匹配條件(如 Host、Path 等),決定指令碼對哪些請求生效
- 撰寫你的攔截邏輯
- 啟用指令碼後,開始抓包即可生效
指令碼基本結構
每個指令碼可以包含兩個函數:
// 請求攔截(可選)
function interceptRequest(request) {
// 你的邏輯
return { action: 'passthrough' };
}
// 回應攔截(可選)
function interceptResponse(request, response) {
// 你的邏輯
return { action: 'passthrough' };
}
- 你可以只定義其中一個,也可以兩個都定義
- 指令碼按照優先順序和建立時間順序執行
- 一旦某個指令碼返回非
passthrough的結果,後續指令碼將不再執行
請求攔截函數 interceptRequest
當匹配的 HTTP 請求到達時,interceptRequest 函數會被呼叫。
參數:request 物件
| 屬性 | 型別 | 說明 |
|---|---|---|
method | string | HTTP 方法(GET, POST, PUT, DELETE 等) |
url | string | 完整 URL |
host | string | 主機名稱 |
port | number | 連接埠號 |
path | string | 請求路徑 |
scheme | string | 協定(http / https) |
headers | object | 請求標頭(key-value 物件) |
body | string | 請求主體字串 |
queryParams | object | URL 查詢參數(key-value 物件) |
返回值
返回一個物件,包含 action 欄位來決定如何處理請求:
1. 放行請求(passthrough)
return { action: 'passthrough' };
不做任何修改,請求正常發送。
2. 修改請求(modify)
request.headers['Authorization'] = 'Bearer my-token';
request.body = JSON.stringify({ modified: true });
return { action: 'modify', request: request };
修改請求內容後繼續發送。
3. 模擬回應(mock)
return {
action: 'mock',
response: {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ success: true, data: 'mocked' })
}
};
不發送真實請求,直接返回模擬的回應。
4. 重定向(redirect)
return {
action: 'redirect',
url: 'https://new-api.example.com' + request.path,
statusCode: 302 // 可選,預設 302
};
將請求重定向到另一個 URL。
5. 丟棄請求(drop)
return { action: 'drop' };
直接丟棄請求,不發送也不回應。
回應攔截函數 interceptResponse
當伺服器返回回應後,interceptResponse 函數會被呼叫。
參數
- request:原始請求資訊(同上)
- response:回應資訊
| 屬性 | 型別 | 說明 |
|---|---|---|
statusCode | number | HTTP 狀態碼 |
headers | object | 回應標頭(key-value 物件) |
body | string | 回應主體字串 |
返回值
1. 放行回應(passthrough)
return { action: 'passthrough' };
2. 修改回應(modify)
var data = JSON.parse(response.body);
data.injected = true;
response.body = JSON.stringify(data);
return { action: 'modify', response: response };
3. 延遲回應(delay)
return {
action: 'delay',
response: response,
delay: 3000 // 延遲 3000 毫秒
};
4. 丟棄回應(drop)
return { action: 'drop' };
內建 API
指令碼環境中預置了以下全域物件,可在 interceptRequest 和 interceptResponse 中直接使用。
httpClient — HTTP 請求
在指令碼中發起 HTTP/HTTPS 請求。呼叫是 同步 的,指令碼會等待請求完成後繼續執行。每個請求的耗時會自動記錄到日誌中,方便排查效能問題。
方法
// GET 請求
httpClient.get(url)
httpClient.get(url, headers)
// POST 請求
httpClient.post(url)
httpClient.post(url, body)
httpClient.post(url, body, headers)
// PUT 請求
httpClient.put(url)
httpClient.put(url, body)
httpClient.put(url, body, headers)
// DELETE 請求
httpClient.delete(url)
httpClient.delete(url, headers)
參數說明
| 參數 | 型別 | 必填 | 說明 |
|---|---|---|---|
url | string | ✅ | 完整的請求 URL(http:// 或 https://) |
body | string | ❌ | 請求主體字串(JSON 請使用 JSON.stringify()) |
headers | object | ❌ | 請求標頭物件 |
返回值
所有方法返回相同結構的回應物件:
{
statusCode: 200, // HTTP 狀態碼(請求失敗時為 -1)
headers: { ... }, // 回應標頭物件
body: "...", // 回應主體字串
error: "" // 錯誤資訊(成功時為空字串)
}
範例
// 簡單 GET 請求
var resp = httpClient.get('https://api.example.com/status');
console.log('Status: ' + resp.statusCode);
// 帶請求標頭的 GET 請求
var resp = httpClient.get('https://api.example.com/user', {
'Authorization': 'Bearer my-token'
});
// POST JSON 資料
var resp = httpClient.post(
'https://api.example.com/data',
JSON.stringify({ name: 'test', value: 123 }),
{ 'Content-Type': 'application/json' }
);
// 檢查請求是否成功
if (resp.error) {
console.error('請求失敗: ' + resp.error);
} else {
var data = JSON.parse(resp.body);
console.log('回應資料: ' + JSON.stringify(data));
}
自動耗時日誌
每次 httpClient 呼叫完成後,引擎會自動輸出日誌:
[Script] httpClient.get https://api.example.com/status -> 200 (156ms)
[Script] httpClient.post https://api.example.com/data -> 201 (342ms)
你可以在 APICatcher 的日誌面板中檢視這些資訊,幫助定位慢請求。
⚠️ 注意:httpClient 的呼叫是同步的,會阻塞指令碼執行直到 HTTP 請求完成(逾時時間 15 秒)。如果你在指令碼中發起耗時的 HTTP 請求,可能會導致原始被攔截請求的回應延遲。請關注日誌中的耗時資料。
localStore — 本地儲存
提供簡單的 Key-Value 本地持久化儲存。資料儲存在 App 的 UserDefaults 中,所有指令碼共享同一儲存空間。如果需要按 Host 或其他維度隔離資料,請在 key 中自行加入前綴(例如 request.host + '_token')。
方法
// 寫入資料
localStore.write(key, value)
// 讀取資料
localStore.read(key) // 返回 string 或 null
// 刪除資料
localStore.remove(key)
參數說明
| 參數 | 型別 | 說明 |
|---|---|---|
key | string | 儲存的鍵名 |
value | any | 儲存的值(將自動轉換為字串) |
儲存說明
- 所有指令碼共享同一儲存空間
- 資料持久化在 App 的沙盒中(本地儲存),App 重啟後依然存在
- 如需按 Host 隔離,請在 key 中加入 Host 資訊,例如:
// 手動按 host 隔離
var key = request.host + '_token';
localStore.write(key, 'my-token');
範例
// 基本讀寫
localStore.write('counter', '1');
var count = localStore.read('counter'); // '1'
console.log('Count: ' + count);
// 儲存 JSON 物件
var config = { debug: true, maxRetry: 3 };
localStore.write('config', JSON.stringify(config));
// 讀取 JSON 物件
var raw = localStore.read('config');
if (raw) {
var config = JSON.parse(raw);
console.log('Debug mode: ' + config.debug);
}
// 刪除資料
localStore.remove('counter');
var v = localStore.read('counter'); // null
console — 日誌輸出
用於在指令碼中輸出日誌,可在 API Catcher 的日誌面板中檢視。
console.log('普通日誌'); // 資訊級別
console.warn('警告日誌'); // 警告級別
console.error('錯誤日誌'); // 錯誤級別
支援多個參數,物件會自動 JSON 序列化:
console.log('請求資訊:', request.method, request.url);
console.log('回應資料:', { status: response.statusCode, body: response.body });
實戰範例
範例 1:自動注入認證 Token
攔截所有 api.myserver.com 的請求,自動添加 Authorization 頭。Token 從登入介面獲取並快取到 localStore。
function interceptRequest(request) {
// 只處理目標伺服器
if (!request.host.includes('api.myserver.com')) {
return { action: 'passthrough' };
}
// 嘗試從快取讀取 token
var token = localStore.read('auth_token');
var tokenExpiry = localStore.read('auth_token_expiry');
// 檢查 token 是否過期(使用時間戳記,單位秒)
var now = Math.floor(Date.now() / 1000);
if (!token || !tokenExpiry || now > parseInt(tokenExpiry)) {
// Token 不存在或已過期,重新登入獲取
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;
// 快取 token,設定 1 小時過期
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' };
}
}
// 注入 Authorization 頭
request.headers['Authorization'] = 'Bearer ' + token;
return { action: 'modify', request: request };
}
範例 2:介面資料轉發/日誌上報
將攔截到的請求和回應資訊上報到你的日誌分析伺服器。
function interceptResponse(request, response) {
// 只上報特定路徑的介面
if (!request.path.startsWith('/api/v2/')) {
return { action: 'passthrough' };
}
// 收集請求和回應資訊
var logData = {
timestamp: new Date().toISOString(),
request: {
method: request.method,
url: request.url,
headers: request.headers
},
response: {
statusCode: response.statusCode,
body: response.body
}
};
// 上報到日誌伺服器(注意:請求是同步的,會阻塞目前回應的返回)
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);
}
// 放行原始回應
return { action: 'passthrough' };
}
範例 3:A/B 測試 — 動態修改回應
使用計數器實作簡單的 A/B 分流,交替返回不同的模擬資料。
function interceptRequest(request) {
if (request.path !== '/api/recommend') {
return { action: 'passthrough' };
}
// 從 localStore 讀取交替計數器
var count = parseInt(localStore.read('ab_counter') || '0');
count++;
localStore.write('ab_counter', String(count));
// 奇偶交替返回不同版本
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: '推薦方案A-1' }, { id: 2, name: '推薦方案A-2' }]
})
}
};
} else {
return {
action: 'mock',
response: {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
version: 'B',
items: [{ id: 3, name: '推薦方案B-1' }, { id: 4, name: '推薦方案B-2' }]
})
}
};
}
}
範例 4:請求限流保護
記錄同一介面的請求頻率,超過閾值時返回 429 錯誤。
function interceptRequest(request) {
if (!request.path.startsWith('/api/')) {
return { action: 'passthrough' };
}
var key = 'rate_' + request.path;
var now = Math.floor(Date.now() / 1000);
// 讀取上次請求的時間和計數
var rawData = localStore.read(key);
var data = rawData ? JSON.parse(rawData) : { count: 0, windowStart: now };
// 如果時間視窗超過 60 秒,重置計數器
if (now - data.windowStart > 60) {
data = { count: 0, windowStart: now };
}
data.count++;
localStore.write(key, JSON.stringify(data));
// 每分鐘最多 10 次
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' };
}
注意事項
-
httpClient 是同步呼叫:指令碼中發起的 HTTP 請求會阻塞指令碼執行,直到請求完成或逾時(15 秒)。這會影響原始請求的處理速度。請關注日誌中自動輸出的請求耗時資訊。
-
localStore 共享儲存:所有指令碼共享同一儲存空間。如果需要按 Host 隔離資料,請在 Key 中自行加入前綴。
-
指令碼執行順序:多個指令碼按優先順序排序執行。一旦某個指令碼返回非
passthrough的結果,後續指令碼將跳過。 -
遠端指令碼:支援從 URL 載入指令碼。啟動抓包時會自動拉取遠端指令碼內容。如果拉取失敗,該指令碼會被跳過。
-
JSON 處理:請求/回應的 body 都是字串。需要處理 JSON 資料時,使用
JSON.parse()解析、JSON.stringify()序列化。 -
錯誤處理:建議在關鍵邏輯中使用
try-catch,並透過console.error()記錄例外,避免指令碼崩潰導致攔截鏈中斷。 -
輔助函數:引擎預置了兩個工具函數:
safeJsonParse(str)— 安全的 JSON 解析,失敗返回 nulldeepClone(obj)— 深拷貝物件