254 lines
7.6 KiB
JavaScript
254 lines
7.6 KiB
JavaScript
// src/debug/apiDebugger.js
|
|
/**
|
|
* API 调试工具
|
|
* 生产环境临时调试使用,后期可整体删除 src/debug/ 目录
|
|
*/
|
|
|
|
import axios from 'axios';
|
|
import { getApiBase } from '@utils/apiConfig';
|
|
|
|
class ApiDebugger {
|
|
constructor() {
|
|
this.requestLog = [];
|
|
this.maxLogSize = 100;
|
|
this.isLogging = true;
|
|
}
|
|
|
|
/**
|
|
* 初始化 Axios 拦截器
|
|
*/
|
|
init() {
|
|
// 请求拦截器
|
|
axios.interceptors.request.use(
|
|
(config) => {
|
|
if (this.isLogging) {
|
|
const logEntry = {
|
|
type: 'request',
|
|
timestamp: new Date().toISOString(),
|
|
method: config.method.toUpperCase(),
|
|
url: config.url,
|
|
baseURL: config.baseURL,
|
|
fullURL: this._getFullURL(config),
|
|
headers: config.headers,
|
|
data: config.data,
|
|
params: config.params,
|
|
};
|
|
|
|
this._addLog(logEntry);
|
|
|
|
console.log(
|
|
`%c[API Request] ${logEntry.method} ${logEntry.fullURL}`,
|
|
'color: #2196F3; font-weight: bold;',
|
|
{
|
|
headers: config.headers,
|
|
data: config.data,
|
|
params: config.params,
|
|
}
|
|
);
|
|
}
|
|
return config;
|
|
},
|
|
(error) => {
|
|
console.error('[API Request Error]', error);
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// 响应拦截器
|
|
axios.interceptors.response.use(
|
|
(response) => {
|
|
if (this.isLogging) {
|
|
const logEntry = {
|
|
type: 'response',
|
|
timestamp: new Date().toISOString(),
|
|
method: response.config.method.toUpperCase(),
|
|
url: response.config.url,
|
|
fullURL: this._getFullURL(response.config),
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
headers: response.headers,
|
|
data: response.data,
|
|
};
|
|
|
|
this._addLog(logEntry);
|
|
|
|
console.log(
|
|
`%c[API Response] ${logEntry.method} ${logEntry.fullURL} - ${logEntry.status}`,
|
|
'color: #4CAF50; font-weight: bold;',
|
|
{
|
|
status: response.status,
|
|
data: response.data,
|
|
headers: response.headers,
|
|
}
|
|
);
|
|
}
|
|
return response;
|
|
},
|
|
(error) => {
|
|
if (this.isLogging) {
|
|
const logEntry = {
|
|
type: 'error',
|
|
timestamp: new Date().toISOString(),
|
|
method: error.config?.method?.toUpperCase() || 'UNKNOWN',
|
|
url: error.config?.url || 'UNKNOWN',
|
|
fullURL: error.config ? this._getFullURL(error.config) : 'UNKNOWN',
|
|
status: error.response?.status,
|
|
statusText: error.response?.statusText,
|
|
message: error.message,
|
|
data: error.response?.data,
|
|
};
|
|
|
|
this._addLog(logEntry);
|
|
|
|
console.error(
|
|
`%c[API Error] ${logEntry.method} ${logEntry.fullURL}`,
|
|
'color: #F44336; font-weight: bold;',
|
|
{
|
|
status: error.response?.status,
|
|
message: error.message,
|
|
data: error.response?.data,
|
|
}
|
|
);
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
console.log('%c[API Debugger] Initialized', 'color: #FF9800; font-weight: bold;');
|
|
}
|
|
|
|
/**
|
|
* 获取完整 URL
|
|
*/
|
|
_getFullURL(config) {
|
|
const baseURL = config.baseURL || '';
|
|
const url = config.url || '';
|
|
const fullURL = baseURL + url;
|
|
|
|
// 添加查询参数
|
|
if (config.params) {
|
|
const params = new URLSearchParams(config.params).toString();
|
|
return params ? `${fullURL}?${params}` : fullURL;
|
|
}
|
|
|
|
return fullURL;
|
|
}
|
|
|
|
/**
|
|
* 添加日志
|
|
*/
|
|
_addLog(entry) {
|
|
this.requestLog.unshift(entry);
|
|
if (this.requestLog.length > this.maxLogSize) {
|
|
this.requestLog = this.requestLog.slice(0, this.maxLogSize);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取所有日志
|
|
*/
|
|
getLogs(type = 'all') {
|
|
if (type === 'all') {
|
|
return this.requestLog;
|
|
}
|
|
return this.requestLog.filter((log) => log.type === type);
|
|
}
|
|
|
|
/**
|
|
* 清空日志
|
|
*/
|
|
clearLogs() {
|
|
this.requestLog = [];
|
|
console.log('[API Debugger] Logs cleared');
|
|
}
|
|
|
|
/**
|
|
* 导出日志为 JSON
|
|
*/
|
|
exportLogs() {
|
|
const blob = new Blob([JSON.stringify(this.requestLog, null, 2)], {
|
|
type: 'application/json',
|
|
});
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `api-logs-${Date.now()}.json`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
console.log('[API Debugger] Logs exported');
|
|
}
|
|
|
|
/**
|
|
* 打印日志统计
|
|
*/
|
|
printStats() {
|
|
const stats = {
|
|
total: this.requestLog.length,
|
|
requests: this.requestLog.filter((log) => log.type === 'request').length,
|
|
responses: this.requestLog.filter((log) => log.type === 'response').length,
|
|
errors: this.requestLog.filter((log) => log.type === 'error').length,
|
|
};
|
|
|
|
console.table(stats);
|
|
return stats;
|
|
}
|
|
|
|
/**
|
|
* 手动发送 API 请求(测试用)
|
|
*/
|
|
async testRequest(method, endpoint, data = null, config = {}) {
|
|
const apiBase = getApiBase();
|
|
const url = `${apiBase}${endpoint}`;
|
|
|
|
console.log(`[API Debugger] Testing ${method.toUpperCase()} ${url}`);
|
|
|
|
try {
|
|
const response = await axios({
|
|
method,
|
|
url,
|
|
data,
|
|
...config,
|
|
});
|
|
|
|
console.log('[API Debugger] Test succeeded:', response.data);
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('[API Debugger] Test failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开启/关闭日志记录
|
|
*/
|
|
toggleLogging(enabled) {
|
|
this.isLogging = enabled;
|
|
console.log(`[API Debugger] Logging ${enabled ? 'enabled' : 'disabled'}`);
|
|
}
|
|
|
|
/**
|
|
* 获取最近的错误
|
|
*/
|
|
getRecentErrors(count = 10) {
|
|
return this.requestLog.filter((log) => log.type === 'error').slice(0, count);
|
|
}
|
|
|
|
/**
|
|
* 按 URL 过滤日志
|
|
*/
|
|
getLogsByURL(urlPattern) {
|
|
return this.requestLog.filter((log) => log.url && log.url.includes(urlPattern));
|
|
}
|
|
|
|
/**
|
|
* 按状态码过滤日志
|
|
*/
|
|
getLogsByStatus(status) {
|
|
return this.requestLog.filter((log) => log.status === status);
|
|
}
|
|
}
|
|
|
|
// 导出单例
|
|
export const apiDebugger = new ApiDebugger();
|
|
export default apiDebugger;
|