// ==UserScript== // @name 网络请求分析工具 // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 网络异常分析工具,可以分析网络请求失败的原因并提供专业的排查建议 // @author Wangshiwei // @match *://*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=bing.com // @license MIT // @grant GM_xmlhttpRequest // ==/UserScript== (function() { 'use strict'; const CUSTOM_ICON = `` // 获取保存的位置 function getSavedPosition() { try { const saved = localStorage.getItem('network-analysis-btn-position'); if (saved) { const pos = JSON.parse(saved); return { x: pos.x, y: pos.y }; } } catch (e) { // 忽略错误 } return null; } // 保存位置 function savePosition(x, y) { try { localStorage.setItem('network-analysis-btn-position', JSON.stringify({ x, y })); } catch (e) { // 忽略错误 } } // 创建悬浮按钮 function createFloatingButton() { // 检查是否已经存在,防止重复创建 if (document.getElementById('network-analysis-container')) { return; } const container = document.createElement('div'); container.id = 'network-analysis-container'; // 获取保存的位置或使用默认位置(右侧中间) const savedPos = getSavedPosition(); const defaultX = window.innerWidth - 92; // 右侧(72px按钮 + 20px边距) const defaultY = window.innerHeight / 2 - 36; // 中间(72px按钮的一半) container.style.cssText = ` position: fixed; right: ${savedPos ? 'auto' : '20px'}; left: ${savedPos ? savedPos.x + 'px' : 'auto'}; top: ${savedPos ? savedPos.y + 'px' : '50%'}; transform: ${savedPos ? 'none' : 'translateY(-50%)'}; width: 72px; z-index: 10000; user-select: none; `; const button = document.createElement('div'); button.id = 'network-analysis-btn'; // 默认网络图标 SVG(如果用户未提供自定义图标则使用此图标) const defaultIconSVG = ` `; // 使用自定义图标(如果提供),否则使用默认图标 const iconHTML = CUSTOM_ICON && CUSTOM_ICON.trim() ? CUSTOM_ICON.trim() : defaultIconSVG; button.innerHTML = `
×
${iconHTML}
`; button.style.cssText = ` position:fixed; right:0px; width: 48px; height: 72px; background: #fff; border-radius: 12px; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1); transition: box-shadow 0.3s ease, transform 0.2s ease; user-select: none; border: 1px solid #f0f0f0; opacity: 0.6; `; // 关闭按钮事件 const closeBtn = button.querySelector('#close-btn'); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); container.style.display = 'none'; }); closeBtn.addEventListener('mouseenter', () => { closeBtn.style.background = '#f5f5f5'; closeBtn.style.color = '#333'; closeBtn.style.borderColor = '#d0d0d0'; }); closeBtn.addEventListener('mouseleave', () => { closeBtn.style.background = '#fff'; closeBtn.style.color = '#999'; closeBtn.style.borderColor = '#e0e0e0'; }); // 点击按钮打开弹窗(排除关闭按钮) button.addEventListener('click', (e) => { if (e.target.id !== 'close-btn' && !e.target.closest('#close-btn')) { toggleModal(); } }); // 拖动功能 let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; // 初始化偏移量 if (savedPos) { xOffset = savedPos.x; yOffset = savedPos.y; } else { xOffset = defaultX; yOffset = defaultY; } button.addEventListener('mousedown', (e) => { if (e.target.id === 'close-btn' || e.target.closest('#close-btn')) { return; } initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; if (e.button === 0) { // 左键 isDragging = true; button.style.cursor = 'grabbing'; button.style.boxShadow = '0 8px 24px rgba(0, 0, 0, 0.25), 0 4px 8px rgba(0, 0, 0, 0.15)'; button.style.transform = 'scale(1.05)'; } }); document.addEventListener('mousemove', (e) => { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; setTranslate(currentX, currentY, container); } }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; button.style.cursor = 'move'; button.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1)'; button.style.transform = 'scale(1)'; // 保存位置 const rect = container.getBoundingClientRect(); savePosition(rect.left, rect.top); } }); // 触摸事件支持 button.addEventListener('touchstart', (e) => { if (e.target.id === 'close-btn' || e.target.closest('#close-btn')) { return; } const touch = e.touches[0]; initialX = touch.clientX - xOffset; initialY = touch.clientY - yOffset; isDragging = true; }); document.addEventListener('touchmove', (e) => { if (isDragging) { e.preventDefault(); const touch = e.touches[0]; currentX = touch.clientX - initialX; currentY = touch.clientY - initialY; xOffset = currentX; yOffset = currentY; setTranslate(currentX, currentY, container); } }); document.addEventListener('touchend', () => { if (isDragging) { isDragging = false; const rect = container.getBoundingClientRect(); savePosition(rect.left, rect.top); } }); function setTranslate(xPos, yPos, el) { // 限制在可视区域内 const maxX = window.innerWidth - el.offsetWidth; const maxY = window.innerHeight - el.offsetHeight; xPos = Math.max(0, Math.min(xPos, maxX)); yPos = Math.max(0, Math.min(yPos, maxY)); el.style.left = xPos + 'px'; el.style.top = yPos + 'px'; el.style.right = 'auto'; el.style.transform = 'none'; } // 如果已保存位置,应用它;否则使用默认位置 if (savedPos) { setTranslate(savedPos.x, savedPos.y, container); } else { // 延迟设置默认位置,确保窗口尺寸已确定 setTimeout(() => { const defaultX = window.innerWidth - 92; const defaultY = window.innerHeight / 2 - 36; setTranslate(defaultX, defaultY, container); savePosition(defaultX, defaultY); }, 100); } // 窗口大小改变时调整位置 window.addEventListener('resize', () => { const rect = container.getBoundingClientRect(); const maxX = window.innerWidth - container.offsetWidth; const maxY = window.innerHeight - container.offsetHeight; let newX = rect.left; let newY = rect.top; if (newX > maxX) newX = maxX; if (newY > maxY) newY = maxY; if (newX < 0) newX = 0; if (newY < 0) newY = 0; if (newX !== rect.left || newY !== rect.top) { setTranslate(newX, newY, container); savePosition(newX, newY); } }); container.appendChild(button); document.body.appendChild(container); return container; } // 创建弹窗 function createModal() { // 检查是否已经存在,防止重复创建 if (document.getElementById('network-analysis-modal')) { return; } const modal = document.createElement('div'); modal.id = 'network-analysis-modal'; modal.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 600px; max-width: 90vw; background: white; border-radius: 12px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); z-index: 10001; display: none; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; `; modal.innerHTML = `

网络请求分析工具

📊
请输入URL并点击"发送请求"开始分析
`; // 添加加载动画样式 const style = document.createElement('style'); style.textContent = ` @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } #url-input:focus { border-color: #667eea; } #send-request-btn:hover { opacity: 0.9; } #send-request-btn:disabled { opacity: 0.5; cursor: not-allowed; } #get-from-page-btn:hover, #clear-config-btn:hover { opacity: 0.9; } #result-content::-webkit-scrollbar { width: 8px; } #result-content::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } #result-content::-webkit-scrollbar-thumb { background: #888; border-radius: 4px; } #result-content::-webkit-scrollbar-thumb:hover { background: #555; } `; document.head.appendChild(style); document.body.appendChild(modal); // 绑定事件 document.getElementById('close-modal-btn').addEventListener('click', () => { toggleModal(); }); document.getElementById('send-request-btn').addEventListener('click', () => { handleRequest(); }); document.getElementById('url-input').addEventListener('keypress', (e) => { if (e.key === 'Enter') { handleRequest(); } }); // 从当前页面获取请求头和Cookie document.getElementById('get-from-page-btn').addEventListener('click', () => { getFromCurrentPage(); }); // 清除配置 document.getElementById('clear-config-btn').addEventListener('click', () => { document.getElementById('headers-input').value = ''; document.getElementById('cookie-input').value = ''; }); // 点击遮罩层关闭 modal.addEventListener('click', (e) => { if (e.target === modal) { toggleModal(); } }); return modal; } // 从当前页面获取请求头和Cookie function getFromCurrentPage() { const headersInput = document.getElementById('headers-input'); const cookieInput = document.getElementById('cookie-input'); // 获取当前页面的Cookie const cookies = document.cookie; if (cookies) { cookieInput.value = cookies; } const headers = []; const commonHeaders = {}; // 添加常见的请求头 commonHeaders['Accept'] = 'application/json, text/plain, */*'; commonHeaders['Accept-Language'] = navigator.language || 'zh-CN,zh;q=0.9'; commonHeaders['User-Agent'] = navigator.userAgent; commonHeaders['Referer'] = window.location.href; commonHeaders['Origin'] = window.location.origin; commonHeaders['Connection'] = 'keep-alive'; commonHeaders['Sec-Fetch-Dest'] = 'empty'; commonHeaders['Sec-Fetch-Mode'] = 'cors'; commonHeaders['Sec-Fetch-Site'] = 'same-origin'; // 检查Cookie中是否有token信息 if (cookies) { const cookiePairs = cookies.split(';'); for (const pair of cookiePairs) { const trimmedPair = pair.trim(); const equalIndex = trimmedPair.indexOf('='); if (equalIndex <= 0) continue; const key = trimmedPair.substring(0, equalIndex).trim(); let value = trimmedPair.substring(equalIndex + 1).trim(); if (key && (key.toLowerCase().includes('token') || key.toLowerCase().includes('auth'))) { // 尝试URL解码 try { value = decodeURIComponent(value); } catch (e) { // 如果解码失败,尝试部分替换 value = value.replace(/%20/g, ' ').replace(/%2B/g, '+'); } // 检查是否已经包含 "token " 或 "Bearer " if (value.startsWith('token ') || value.startsWith('Bearer ')) { commonHeaders['Authorization'] = value; break; } else if (value && value.length > 0 && value.length < 500) { // 如果值看起来像token(长度合理,不包含特殊字符) if (!value.includes('{') && !value.includes('[') && !value.includes('"')) { commonHeaders['Authorization'] = `token ${value}`; break; } } } } } // 检查localStorage和sessionStorage中的token try { const storageKeys = ['token', 'auth', 'authorization', 'access_token', 'api_token', 'bearer_token']; // 检查localStorage for (const key of storageKeys) { const value = localStorage.getItem(key); if (value && value.trim()) { if (value.startsWith('token ') || value.startsWith('Bearer ')) { commonHeaders['Authorization'] = value; break; } else if (!value.includes('{') && !value.includes('[') && value.length < 200) { commonHeaders['Authorization'] = `token ${value}`; break; } } } // 如果localStorage没找到,检查sessionStorage if (!commonHeaders['Authorization']) { for (const key of storageKeys) { const value = sessionStorage.getItem(key); if (value && value.trim()) { if (value.startsWith('token ') || value.startsWith('Bearer ')) { commonHeaders['Authorization'] = value; break; } else if (!value.includes('{') && !value.includes('[') && value.length < 200) { commonHeaders['Authorization'] = `token ${value}`; break; } } } } // 检查所有localStorage键,查找包含token的 if (!commonHeaders['Authorization']) { for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key && (key.toLowerCase().includes('token') || key.toLowerCase().includes('auth'))) { const value = localStorage.getItem(key); if (value && value.trim() && !value.includes('{') && !value.includes('[') && value.length < 200) { if (value.startsWith('token ') || value.startsWith('Bearer ')) { commonHeaders['Authorization'] = value; } else { commonHeaders['Authorization'] = `token ${value}`; } break; } } } } } catch (e) { // 忽略错误 } // 格式化请求头 for (const [key, value] of Object.entries(commonHeaders)) { headers.push(`${key}: ${value}`); } headersInput.value = headers.join('\n'); // 提示用户 const btn = document.getElementById('get-from-page-btn'); const originalText = btn.textContent; btn.textContent = '已获取 ✓'; btn.style.background = '#27ae60'; setTimeout(() => { btn.textContent = originalText; btn.style.background = '#667eea'; }, 2000); } // 切换弹窗显示/隐藏 function toggleModal() { const modal = document.getElementById('network-analysis-modal'); if (modal.style.display === 'none' || !modal.style.display) { modal.style.display = 'flex'; // 创建遮罩层 if (!document.getElementById('modal-overlay')) { const overlay = document.createElement('div'); overlay.id = 'modal-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 10000; `; overlay.addEventListener('click', () => { toggleModal(); }); document.body.appendChild(overlay); } } else { modal.style.display = 'none'; const overlay = document.getElementById('modal-overlay'); if (overlay) { overlay.remove(); } } } // 解析请求头字符串 function parseHeaders(headersText) { const headers = {}; if (!headersText || !headersText.trim()) { return headers; } const lines = headersText.split('\n'); lines.forEach(line => { line = line.trim(); if (!line) return; const colonIndex = line.indexOf(':'); if (colonIndex > 0) { const key = line.substring(0, colonIndex).trim(); const value = line.substring(colonIndex + 1).trim(); if (key && value) { headers[key] = value; } } }); return headers; } // 处理请求 function handleRequest() { const urlInput = document.getElementById('url-input'); const headersInput = document.getElementById('headers-input'); const cookieInput = document.getElementById('cookie-input'); const url = urlInput.value.trim(); const sendBtn = document.getElementById('send-request-btn'); const resultContent = document.getElementById('result-content'); if (!url) { alert('请输入URL'); return; } // 验证URL格式 try { new URL(url); } catch (e) { alert('URL格式不正确,请输入完整的URL(包含协议,如 https://)'); return; } // 解析请求头和Cookie const headers = parseHeaders(headersInput.value); const cookie = cookieInput.value.trim(); // 显示加载状态(只在结果区域内显示) sendBtn.disabled = true; resultContent.innerHTML = '
正在分析中...
'; // 发送请求 const startTime = Date.now(); // 构建请求配置 const requestConfig = { method: 'GET', url: url, timeout: 30000, onload: function(response) { const endTime = Date.now(); const duration = endTime - startTime; sendBtn.disabled = false; let analysis = analyzeResponse(response, duration, url); displayResult(analysis); }, onerror: function(error) { const endTime = Date.now(); const duration = endTime - startTime; sendBtn.disabled = false; let analysis = analyzeError(error, duration, url); displayResult(analysis); }, ontimeout: function() { sendBtn.disabled = false; let analysis = analyzeTimeout(url); displayResult(analysis); } }; // 如果有请求头或Cookie,添加到请求中 if (Object.keys(headers).length > 0 || cookie) { requestConfig.headers = { ...headers }; if (cookie) { requestConfig.headers['Cookie'] = cookie; } } GM_xmlhttpRequest(requestConfig); } // 格式化字节大小 function formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; } // 解析URL结构 function parseUrlStructure(url) { try { const urlObj = new URL(url); return { protocol: urlObj.protocol, host: urlObj.hostname, port: urlObj.port || (urlObj.protocol === 'https:' ? '443' : urlObj.protocol === 'http:' ? '80' : 'Default'), path: urlObj.pathname, query: urlObj.search ? urlObj.search.substring(1) : 'None', hash: urlObj.hash ? urlObj.hash.substring(1) : 'None', fullUrl: url }; } catch (e) { return null; } } // 解析响应头 function parseHeaders(headersString) { const headers = {}; if (!headersString) return headers; const lines = headersString.split(/\r?\n/); lines.forEach(line => { const colonIndex = line.indexOf(':'); if (colonIndex > 0) { const key = line.substring(0, colonIndex).trim().toLowerCase(); const value = line.substring(colonIndex + 1).trim(); if (key && value) { if (headers[key]) { // 如果已存在,转换为数组 if (Array.isArray(headers[key])) { headers[key].push(value); } else { headers[key] = [headers[key], value]; } } else { headers[key] = value; } } } }); return headers; } // 分析响应 function analyzeResponse(response, duration, url) { const status = response.status; const statusText = response.statusText; const headersString = response.responseHeaders || ''; const responseText = response.responseText || ''; const responseLength = responseText ? new Blob([responseText]).size : 0; // 解析URL结构 const urlStructure = parseUrlStructure(url); // 解析响应头 const headers = parseHeaders(headersString); let analysis = { url: url, urlStructure: urlStructure, status: status, statusText: statusText, duration: duration, isError: status >= 400, issues: [], suggestions: [], responseHeaders: headers, responseHeadersRaw: headersString, responseLength: responseLength, responseLengthFormatted: formatBytes(responseLength), contentType: headers['content-type'] || 'Unknown', corsConfig: {}, details: {} }; // 分析HTTP状态码 if (status >= 200 && status < 300) { analysis.issues.push('请求成功'); analysis.suggestions.push('请求已成功完成,无需修复'); } else if (status >= 300 && status < 400) { analysis.issues.push(`HTTP ${status} 重定向`); analysis.suggestions.push('检查重定向目标URL是否正确'); analysis.suggestions.push('确认是否需要跟随重定向'); } else if (status === 400) { analysis.issues.push('HTTP 400 Bad Request - 请求参数错误'); analysis.suggestions.push('检查请求参数格式是否正确'); analysis.suggestions.push('验证请求体是否符合API要求'); analysis.suggestions.push('检查Content-Type头是否正确'); } else if (status === 401) { analysis.issues.push('HTTP 401 Unauthorized - 未授权'); analysis.suggestions.push('检查是否需要添加认证信息(Token、API Key等)'); analysis.suggestions.push('验证认证信息是否过期'); analysis.suggestions.push('确认用户是否有访问权限'); } else if (status === 403) { analysis.issues.push('HTTP 403 Forbidden - 禁止访问'); analysis.suggestions.push('检查用户权限设置'); analysis.suggestions.push('验证IP地址是否被限制'); analysis.suggestions.push('确认资源访问策略(CORS、防火墙等)'); } else if (status === 404) { analysis.issues.push('HTTP 404 Not Found - 资源不存在'); analysis.suggestions.push('检查URL路径是否正确'); analysis.suggestions.push('验证资源是否已被删除或移动'); analysis.suggestions.push('确认API端点是否存在'); } else if (status === 405) { analysis.issues.push('HTTP 405 Method Not Allowed - 请求方法不允许'); analysis.suggestions.push('检查HTTP方法(GET、POST、PUT、DELETE等)是否正确'); analysis.suggestions.push('查看API文档确认支持的请求方法'); } else if (status === 408) { analysis.issues.push('HTTP 408 Request Timeout - 请求超时'); analysis.suggestions.push('检查网络连接是否稳定'); analysis.suggestions.push('增加请求超时时间'); analysis.suggestions.push('优化请求数据大小'); } else if (status === 429) { analysis.issues.push('HTTP 429 Too Many Requests - 请求过于频繁'); analysis.suggestions.push('实施请求频率限制(Rate Limiting)'); analysis.suggestions.push('检查响应头中的Retry-After字段'); analysis.suggestions.push('使用指数退避策略重试'); } else if (status === 500) { analysis.issues.push('HTTP 500 Internal Server Error - 服务器内部错误'); analysis.suggestions.push('这是服务器端问题,联系服务器管理员'); analysis.suggestions.push('检查服务器日志获取详细错误信息'); analysis.suggestions.push('验证服务器资源是否充足(内存、CPU等)'); } else if (status === 502) { analysis.issues.push('HTTP 502 Bad Gateway - 网关错误'); analysis.suggestions.push('检查上游服务器是否正常运行'); analysis.suggestions.push('验证网关配置是否正确'); analysis.suggestions.push('检查负载均衡器状态'); } else if (status === 503) { analysis.issues.push('HTTP 503 Service Unavailable - 服务不可用'); analysis.suggestions.push('服务器可能正在维护,稍后重试'); analysis.suggestions.push('检查服务器负载是否过高'); analysis.suggestions.push('验证依赖服务是否可用'); } else if (status === 504) { analysis.issues.push('HTTP 504 Gateway Timeout - 网关超时'); analysis.suggestions.push('检查上游服务器响应时间'); analysis.suggestions.push('增加网关超时配置'); analysis.suggestions.push('优化后端处理性能'); } else { analysis.issues.push(`HTTP ${status} ${statusText}`); analysis.suggestions.push('查看HTTP状态码文档了解具体含义'); analysis.suggestions.push('检查服务器返回的错误信息'); } // 分析CORS配置 analysis.corsConfig = { allowOrigin: headers['access-control-allow-origin'] || 'Not Set', allowMethods: headers['access-control-allow-methods'] || 'Not Set', allowHeaders: headers['access-control-allow-headers'] || 'Not Set', allowCredentials: headers['access-control-allow-credentials'] || 'Not Set', maxAge: headers['access-control-max-age'] || 'Not Set' }; // CORS检查 if (!headers['access-control-allow-origin']) { if (status >= 200 && status < 300) { analysis.suggestions.push('注意:响应中未发现CORS头,跨域请求可能被阻止'); } } // Content-Type检查 const contentType = headers['content-type'] || ''; if (contentType) { analysis.details.contentType = contentType; // 检查内容类型是否匹配URL扩展名 if (urlStructure && urlStructure.path) { const pathLower = urlStructure.path.toLowerCase(); if (pathLower.endsWith('.png') || pathLower.endsWith('.jpg') || pathLower.endsWith('.jpeg') || pathLower.endsWith('.gif') || pathLower.endsWith('.webp')) { if (!contentType.includes('image/')) { analysis.issues.push('⚠️ 内容类型不匹配:URL指向图片但响应不是图片类型,可能是错误页面'); } } else if (pathLower.endsWith('.json')) { if (!contentType.includes('application/json')) { analysis.issues.push('⚠️ 内容类型不匹配:URL指向JSON但响应类型不正确'); } } } } // 分析响应时间 if (duration > 5000) { analysis.issues.push(`响应时间较长:${duration}ms`); analysis.suggestions.push('优化服务器性能'); analysis.suggestions.push('检查数据库查询效率'); analysis.suggestions.push('考虑使用CDN加速'); } else if (duration < 100) { analysis.details.performance = '响应速度优秀'; } else if (duration < 1000) { analysis.details.performance = '响应速度良好'; } else if (duration < 3000) { analysis.details.performance = '响应速度一般'; } else { analysis.details.performance = '响应速度较慢'; } // 添加服务器信息 if (headers['server']) { analysis.details.server = headers['server']; } // 添加日期信息 if (headers['date']) { analysis.details.date = headers['date']; } return analysis; } // 分析错误 function analyzeError(error, duration, url) { const urlStructure = parseUrlStructure(url); let analysis = { url: url, urlStructure: urlStructure, status: 'ERROR', statusText: '网络错误', duration: duration, isError: true, issues: [], suggestions: [], responseHeaders: {}, responseHeadersRaw: '', responseLength: 0, responseLengthFormatted: '0 Bytes', contentType: 'Unknown', corsConfig: {}, details: {} }; // 根据错误类型分析 const errorMessage = error.error || ''; const errorString = String(errorMessage).toLowerCase(); if (errorString.includes('network') || errorString.includes('failed to fetch')) { analysis.issues.push('网络连接失败'); analysis.suggestions.push('检查网络连接是否正常'); analysis.suggestions.push('验证URL是否可访问'); analysis.suggestions.push('检查防火墙或代理设置'); analysis.suggestions.push('尝试使用ping或curl测试连接'); } else if (errorString.includes('cors') || errorString.includes('cross-origin')) { analysis.issues.push('CORS跨域请求被阻止'); analysis.suggestions.push('服务器需要设置Access-Control-Allow-Origin响应头'); analysis.suggestions.push('检查请求头是否包含不被允许的自定义头'); analysis.suggestions.push('对于预检请求,确保服务器正确处理OPTIONS请求'); analysis.suggestions.push('如果是开发环境,可以考虑使用代理服务器'); } else if (errorString.includes('ssl') || errorString.includes('certificate')) { analysis.issues.push('SSL/TLS证书错误'); analysis.suggestions.push('检查SSL证书是否有效'); analysis.suggestions.push('验证证书是否过期'); analysis.suggestions.push('确认证书链是否完整'); analysis.suggestions.push('检查系统时间是否正确'); } else if (errorString.includes('dns')) { analysis.issues.push('DNS解析失败'); analysis.suggestions.push('检查域名是否正确'); analysis.suggestions.push('验证DNS服务器是否可用'); analysis.suggestions.push('尝试使用其他DNS服务器(如8.8.8.8)'); analysis.suggestions.push('检查本地hosts文件'); } else if (errorString.includes('timeout')) { analysis.issues.push('请求超时'); analysis.suggestions.push('增加请求超时时间'); analysis.suggestions.push('检查网络延迟'); analysis.suggestions.push('优化请求数据大小'); analysis.suggestions.push('检查服务器响应速度'); } else { analysis.issues.push('未知网络错误'); analysis.suggestions.push('检查浏览器控制台获取详细错误信息'); analysis.suggestions.push('验证URL格式是否正确'); analysis.suggestions.push('尝试使用其他工具(如Postman)测试请求'); analysis.suggestions.push('检查浏览器扩展是否干扰请求'); } return analysis; } // 分析超时 function analyzeTimeout(url) { const urlStructure = parseUrlStructure(url); return { url: url, urlStructure: urlStructure, status: 'TIMEOUT', statusText: '请求超时', duration: 30000, isError: true, issues: ['请求在30秒内未收到响应'], suggestions: [ '检查网络连接是否稳定', '验证服务器是否正常运行', '增加请求超时时间设置', '检查服务器负载是否过高', '优化请求数据,减少传输大小', '考虑使用异步处理或轮询机制', '检查防火墙或代理是否阻止请求' ], responseHeaders: {}, responseHeadersRaw: '', responseLength: 0, responseLengthFormatted: '0 Bytes', contentType: 'Unknown', corsConfig: {}, details: {} }; } // 显示结果 function displayResult(analysis) { const resultContent = document.getElementById('result-content'); let html = ''; // URL结构分析 if (analysis.urlStructure) { html += `
`; html += `
📋 URL结构分析
`; html += `
目标URL:${analysis.url || analysis.urlStructure.fullUrl}
`; html += `
协议:${analysis.urlStructure.protocol}
`; html += `
主机:${analysis.urlStructure.host}
`; html += `
端口:${analysis.urlStructure.port}
`; html += `
路径:${analysis.urlStructure.path}
`; html += `
查询参数:${analysis.urlStructure.query}
`; html += `
哈希:${analysis.urlStructure.hash}
`; html += `
`; } // HTTP响应信息 html += `
`; html += `
📡 HTTP响应信息
`; html += `
状态码:`; if (analysis.isError) { html += `${analysis.status} ${analysis.statusText || ''}`; } else { html += `${analysis.status} ${analysis.statusText || 'Success'}`; } html += `
`; html += `
状态消息:${analysis.statusText || 'N/A'}
`; html += `
响应时间:${analysis.duration}ms
`; html += `
响应数据长度:${analysis.responseLengthFormatted} (${analysis.responseLength} bytes)
`; html += `
内容类型:${analysis.contentType}`; if (analysis.contentType && analysis.contentType !== 'Unknown' && analysis.contentType.includes('text/html') && analysis.urlStructure && (analysis.urlStructure.path.endsWith('.png') || analysis.urlStructure.path.endsWith('.jpg') || analysis.urlStructure.path.endsWith('.jpeg'))) { html += ` ⚠️ 响应不是图片类型,可能是错误页面`; } html += `
`; if (analysis.details.server) { html += `
服务器:${analysis.details.server}
`; } if (analysis.details.date) { html += `
响应日期:${analysis.details.date}
`; } html += `
`; // 响应头列表 if (analysis.responseHeadersRaw) { html += `
`; html += `
📋 响应头列表
`; html += `
`; const headerLines = analysis.responseHeadersRaw.split(/\r?\n/).filter(line => line.trim()); headerLines.forEach(line => { html += `
${escapeHtml(line)}
`; }); html += `
`; html += `
`; } // CORS配置 if (analysis.corsConfig && Object.keys(analysis.corsConfig).length > 0) { html += `
`; html += `
🔒 CORS配置
`; html += `
Access-Control-Allow-Origin:${analysis.corsConfig.allowOrigin}
`; html += `
Access-Control-Allow-Methods:${analysis.corsConfig.allowMethods}
`; html += `
Access-Control-Allow-Headers:${analysis.corsConfig.allowHeaders}
`; html += `
Access-Control-Allow-Credentials:${analysis.corsConfig.allowCredentials}
`; html += `
Access-Control-Max-Age:${analysis.corsConfig.maxAge}
`; html += `
`; } // 问题分析 if (analysis.issues && analysis.issues.length > 0) { html += `
`; html += `
❌ 问题分析
`; html += ``; html += `
`; } // 排查建议 if (analysis.suggestions && analysis.suggestions.length > 0) { html += `
`; html += `
💡 排查建议
`; html += `
    `; analysis.suggestions.forEach((suggestion, index) => { html += `
  1. ${suggestion}
  2. `; }); html += `
`; html += `
`; } // 其他详细信息 if (analysis.details && Object.keys(analysis.details).length > 0) { const detailKeys = Object.keys(analysis.details).filter(key => key !== 'contentType' && key !== 'server' && key !== 'date' && key !== 'performance' ); if (detailKeys.length > 0) { html += `
`; html += `
ℹ️ 其他信息
`; html += `
`; detailKeys.forEach(key => { html += `
${key}: ${analysis.details[key]}
`; }); if (analysis.details.performance) { html += `
性能评估:${analysis.details.performance}
`; } html += `
`; html += `
`; } } resultContent.innerHTML = html; } // HTML转义函数 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 检查是否应该运行脚本 function shouldRunScript() { // 只在主窗口中运行,不在 iframe 中运行 if (window.self !== window.top) { return false; } // 检查是否已经存在按钮容器 if (document.getElementById('network-analysis-container')) { return false; } // 检查是否已经初始化过(防止重复执行) if (window.networkAnalysisToolInitialized) { return false; } return true; } // 初始化 function init() { // 检查是否应该运行 if (!shouldRunScript()) { return; } // 标记为已初始化 window.networkAnalysisToolInitialized = true; createFloatingButton(); createModal(); } // 等待DOM加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();