行业资讯
📅 2026/9/6 13:40:04
JavaScript Fetch API 实战:从基础请求到安全优化的完整指南
JavaScript 调用 API 获取后台数据是前端开发中最核心的技能之一。无论是获取用户信息、加载商品列表还是对接第三方服务API 调用都是现代 Web 应用不可或缺的部分。本文将通过实际案例带你快速掌握 JavaScript 调用 API 的完整流程包括基础请求、错误处理、安全策略和性能优化。最值得关注的是现代 JavaScript 提供了多种 API 调用方式从传统的 XMLHttpRequest 到主流的 Fetch API再到第三方库如 Axios。每种方式都有其适用场景和特点。我们将重点介绍最实用的 Fetch API它支持 Promise 语法代码简洁且在现代浏览器中广泛支持。硬件门槛方面API 调用主要依赖网络环境和浏览器兼容性对本地硬件要求极低。任何能够运行现代浏览器的设备都可以进行 API 调用测试。本文将使用 Chrome 浏览器作为演示环境但所有代码在主流浏览器中均可运行。本文将带你完成以下内容环境准备与基础概念、Fetch API 实战入门、GET/POST 请求详解、错误处理与超时控制、跨域问题解决方案、实际项目案例演示以及性能优化与最佳实践。每个环节都包含可运行的代码示例和常见问题排查方法。1. 核心能力速览能力项说明主要调用方式Fetch API、XMLHttpRequest、Axios 等第三方库浏览器要求支持 ES6 的现代浏览器Chrome 42、Firefox 39、Safari 10.1开发环境任意代码编辑器 现代浏览器无需特殊硬件核心功能GET/POST/PUT/DELETE 请求、请求头设置、参数传递、响应处理错误处理网络错误、HTTP 状态码错误、超时控制、重试机制安全考虑CORS 跨域策略、CSRF 防护、内容安全策略适合场景前后端数据交互、第三方服务集成、实时数据更新2. 适用场景与使用边界JavaScript API 调用适用于多种前端开发场景。最常见的是前后端分离架构中前端通过 API 获取后端数据如用户管理系统中的用户列表获取、电商平台中的商品信息加载等。此外集成第三方服务也是重要应用场景比如调用天气 API 显示实时天气、调用地图 API 展示位置信息等。实时数据更新场景同样依赖 API 调用。社交媒体平台的消息推送、股票交易的实时价格显示、在线协作工具的实时同步等功能都需要通过 API 不断获取最新数据。单页面应用SPA的路由切换和数据加载更是重度依赖 API 调用。然而API 调用也有明确的使用边界。浏览器安全策略限制了跨域请求需要服务端配合设置 CORS 头或使用代理方案。敏感操作如支付、密码修改等不应仅依赖前端 API 调用需要后端进行二次验证。大文件上传下载更适合使用专门的传输方案而非普通 API 调用。3. 环境准备与前置条件开始 API 调用前需要准备基本的开发环境。代码编辑器推荐 VS Code它提供丰富的 JavaScript 支持和插件生态。浏览器选择 Chrome 或 Firefox它们有强大的开发者工具便于调试。创建基础 HTML 文件作为测试环境!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleAPI 调用测试/title /head body div idresult/div script // API 调用代码将在这里编写 /script /body /html对于 API 测试可以使用免费的公共测试接口。JSONPlaceholder 提供模拟的 REST API适合学习和测试获取帖子https://jsonplaceholder.typicode.com/posts/1获取用户https://jsonplaceholder.typicode.com/users/1创建帖子https://jsonplaceholder.typicode.com/posts打开浏览器开发者工具F12切换到 Network 标签页便于观察请求详情和响应数据。4. Fetch API 基础入门Fetch API 是现代 JavaScript 中进行网络请求的首选方法。它基于 Promise 设计语法简洁支持异步操作。最基本的 GET 请求示例// 基础 Fetch GET 请求 fetch(https://jsonplaceholder.typicode.com/posts/1) .then(response { if (!response.ok) { throw new Error(网络响应不正常); } return response.json(); }) .then(data { console.log(获取到的数据:, data); document.getElementById(result).innerHTML 标题: ${data.title}br内容: ${data.body}; }) .catch(error { console.error(请求失败:, error); document.getElementById(result).innerHTML 数据加载失败; });Fetch API 的基本流程包括发起请求 → 检查响应状态 → 解析响应数据 → 处理数据 → 错误捕获。response.ok 属性检查 HTTP 状态码是否在 200-299 范围内这是判断请求是否成功的首要条件。使用 async/await 语法可以让代码更清晰async function fetchData() { try { const response await fetch(https://jsonplaceholder.typicode.com/posts/1); if (!response.ok) { throw new Error(HTTP 错误! 状态码: ${response.status}); } const data await response.json(); console.log(数据获取成功:, data); return data; } catch (error) { console.error(获取数据时出错:, error); throw error; } } // 调用函数 fetchData().then(data { document.getElementById(result).innerHTML 帖子ID: ${data.id}br标题: ${data.title}; });5. 各种HTTP请求方法实战5.1 GET 请求与查询参数GET 请求用于获取数据可以携带查询参数// 带查询参数的 GET 请求 async function searchPosts(keyword) { const url new URL(https://jsonplaceholder.typicode.com/posts); url.searchParams.append(userId, 1); if (keyword) { url.searchParams.append(q, keyword); } try { const response await fetch(url); if (!response.ok) throw new Error(请求失败); const posts await response.json(); console.log(找到 ${posts.length} 条结果); return posts; } catch (error) { console.error(搜索失败:, error); return []; } } // 使用示例 searchPosts(重要).then(posts { posts.forEach(post { console.log(ID: ${post.id}, 标题: ${post.title}); }); });5.2 POST 请求与数据提交POST 请求用于创建新资源需要设置请求头和请求体// 创建新帖子的 POST 请求 async function createPost(title, body, userId 1) { const postData { title: title, body: body, userId: userId }; try { const response await fetch(https://jsonplaceholder.typicode.com/posts, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(postData) }); if (!response.ok) throw new Error(创建失败); const newPost await response.json(); console.log(创建成功, ID:, newPost.id); return newPost; } catch (error) { console.error(创建帖子失败:, error); throw error; } } // 使用示例 createPost(JavaScript API 教程, 这是一篇关于 Fetch API 的详细教程) .then(post { console.log(新帖子:, post); });5.3 PUT 和 DELETE 请求PUT 用于更新资源DELETE 用于删除资源// 更新帖子的 PUT 请求 async function updatePost(postId, newData) { try { const response await fetch(https://jsonplaceholder.typicode.com/posts/${postId}, { method: PUT, headers: { Content-Type: application/json, }, body: JSON.stringify(newData) }); if (!response.ok) throw new Error(更新失败); const updatedPost await response.json(); console.log(更新成功:, updatedPost); return updatedPost; } catch (error) { console.error(更新失败:, error); throw error; } } // 删除帖子的 DELETE 请求 async function deletePost(postId) { try { const response await fetch(https://jsonplaceholder.typicode.com/posts/${postId}, { method: DELETE }); if (!response.ok) throw new Error(删除失败); console.log(删除成功); return true; } catch (error) { console.error(删除失败:, error); throw error; } }6. 高级请求配置与错误处理6.1 完整的请求配置Fetch API 支持丰富的配置选项// 完整的请求配置示例 const requestConfig { method: POST, headers: { Content-Type: application/json, Authorization: Bearer your-token-here, X-Requested-With: XMLHttpRequest }, body: JSON.stringify({ title: 测试标题, content: 测试内容 }), mode: cors, // cors, no-cors, same-origin cache: no-cache, // default, no-cache, reload, force-cache, only-if-cached credentials: same-origin, // omit, same-origin, include redirect: follow, // manual, follow, error referrerPolicy: no-referrer-when-downgrade }; async function advancedFetch(url, config {}) { const defaultConfig { method: GET, headers: { Content-Type: application/json, }, timeout: 10000 // 自定义超时处理 }; const finalConfig { ...defaultConfig, ...config }; // 超时控制 const timeoutPromise new Promise((_, reject) { setTimeout(() reject(new Error(请求超时)), finalConfig.timeout); }); try { const response await Promise.race([ fetch(url, finalConfig), timeoutPromise ]); if (!response.ok) { throw new Error(HTTP ${response.status}: ${response.statusText}); } return await response.json(); } catch (error) { console.error(高级请求失败:, error); throw error; } }6.2 全面的错误处理机制健壮的错误处理是 API 调用的关键class ApiClient { constructor(baseURL, defaultHeaders {}) { this.baseURL baseURL; this.defaultHeaders { Content-Type: application/json, ...defaultHeaders }; } async request(endpoint, options {}) { const url ${this.baseURL}${endpoint}; const config { headers: { ...this.defaultHeaders, ...options.headers }, ...options }; try { const response await fetch(url, config); // 处理不同的 HTTP 状态码 if (response.status 401) { throw new Error(未授权请重新登录); } else if (response.status 403) { throw new Error(权限不足); } else if (response.status 404) { throw new Error(资源不存在); } else if (response.status 500) { throw new Error(服务器错误); } else if (!response.ok) { throw new Error(请求失败: ${response.status}); } // 根据 Content-Type 解析响应 const contentType response.headers.get(content-type); if (contentType contentType.includes(application/json)) { return await response.json(); } else { return await response.text(); } } catch (error) { if (error.name TypeError) { throw new Error(网络连接失败请检查网络设置); } throw error; } } // 重试机制 async requestWithRetry(endpoint, options {}, maxRetries 3) { for (let attempt 1; attempt maxRetries; attempt) { try { return await this.request(endpoint, options); } catch (error) { if (attempt maxRetries) throw error; console.warn(请求失败第 ${attempt} 次重试...); await this.delay(1000 * attempt); // 指数退避 } } } delay(ms) { return new Promise(resolve setTimeout(resolve, ms)); } } // 使用示例 const api new ApiClient(https://jsonplaceholder.typicode.com); api.request(/posts/1) .then(data console.log(data)) .catch(error console.error(API 调用失败:, error));7. 跨域问题与解决方案7.1 CORS 跨域资源共享浏览器同源策略会阻止跨域请求需要服务端设置 CORS 头// 检查 CORS 支持 async function checkCORSSupport() { try { const response await fetch(https://api.example.com/data, { method: OPTIONS // 预检请求 }); const corsHeaders { allowOrigin: response.headers.get(access-control-allow-origin), allowMethods: response.headers.get(access-control-allow-methods), allowHeaders: response.headers.get(access-control-allow-headers) }; console.log(CORS 支持情况:, corsHeaders); return corsHeaders; } catch (error) { console.error(CORS 检查失败:, error); return null; } } // 处理 CORS 错误 function setupCorsErrorHandling() { window.addEventListener(error, (event) { if (event.message.includes(CORS)) { console.error(CORS 错误:, event.error); // 显示用户友好的错误信息 showErrorToUser(跨域请求被阻止请联系管理员); } }); } function showErrorToUser(message) { const errorDiv document.createElement(div); errorDiv.style.cssText position: fixed; top: 20px; right: 20px; background: #f44336; color: white; padding: 15px; border-radius: 5px; z-index: 1000; ; errorDiv.textContent message; document.body.appendChild(errorDiv); setTimeout(() errorDiv.remove(), 5000); }7.2 代理服务器解决方案当无法修改服务端 CORS 配置时可以使用代理// 简单的本地代理方案 class ProxyService { constructor(proxyUrl /proxy) { this.proxyUrl proxyUrl; } async viaProxy(targetUrl, options {}) { const proxyRequest { url: targetUrl, method: options.method || GET, headers: options.headers || {}, body: options.body ? await this.serializeBody(options.body) : null }; try { const response await fetch(this.proxyUrl, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(proxyRequest) }); if (!response.ok) throw new Error(代理请求失败); return await response.json(); } catch (error) { console.error(代理请求错误:, error); throw error; } } async serializeBody(body) { if (typeof body string) return body; if (body instanceof FormData) return Object.fromEntries(body); return body; } } // 使用示例 const proxy new ProxyService(); proxy.viaProxy(https://api.example.com/data) .then(data console.log(通过代理获取的数据:, data));8. 实际项目案例演示8.1 用户管理系统 API 集成完整的前后端分离案例class UserApiService { constructor(baseUrl) { this.baseUrl baseUrl; this.token localStorage.getItem(authToken); } // 用户登录 async login(credentials) { try { const response await fetch(${this.baseUrl}/auth/login, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(credentials) }); if (!response.ok) throw new Error(登录失败); const result await response.json(); this.token result.token; localStorage.setItem(authToken, this.token); return result; } catch (error) { console.error(登录错误:, error); throw error; } } // 获取用户列表 async getUsers(page 1, limit 10) { const query new URLSearchParams({ page, limit }); const response await fetch(${this.baseUrl}/users?${query}, { headers: { Authorization: Bearer ${this.token} } }); if (!response.ok) throw new Error(获取用户列表失败); return await response.json(); } // 创建用户 async createUser(userData) { const response await fetch(${this.baseUrl}/users, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${this.token} }, body: JSON.stringify(userData) }); if (!response.ok) throw new Error(创建用户失败); return await response.json(); } // 上传用户头像 async uploadAvatar(userId, file) { const formData new FormData(); formData.append(avatar, file); const response await fetch(${this.baseUrl}/users/${userId}/avatar, { method: POST, headers: { Authorization: Bearer ${this.token} }, body: formData }); if (!response.ok) throw new Error(上传失败); return await response.json(); } } // UI 集成示例 class UserManager { constructor(apiService) { this.api apiService; this.currentPage 1; } async init() { await this.loadUsers(); this.setupEventListeners(); } async loadUsers() { try { this.showLoading(); const users await this.api.getUsers(this.currentPage); this.renderUsers(users); } catch (error) { this.showError(error.message); } finally { this.hideLoading(); } } renderUsers(users) { const container document.getElementById(users-container); container.innerHTML users.map(user div classuser-card h3${user.name}/h3 p邮箱: ${user.email}/p p角色: ${user.role}/p button onclickuserManager.editUser(${user.id})编辑/button /div ).join(); } showLoading() { document.getElementById(loading).style.display block; } hideLoading() { document.getElementById(loading).style.display none; } showError(message) { const errorDiv document.getElementById(error-message); errorDiv.textContent message; errorDiv.style.display block; setTimeout(() errorDiv.style.display none, 5000); } } // 初始化 const userApi new UserApiService(https://api.example.com); const userManager new UserManager(userApi); userManager.init();8.2 实时数据监控面板展示如何实现实时数据更新class RealTimeMonitor { constructor(apiEndpoint, updateInterval 5000) { this.endpoint apiEndpoint; this.interval updateInterval; this.isMonitoring false; this.retryCount 0; this.maxRetries 5; } start() { this.isMonitoring true; this.monitorLoop(); console.log(开始实时监控...); } stop() { this.isMonitoring false; console.log(停止实时监控); } async monitorLoop() { while (this.isMonitoring) { try { await this.fetchData(); this.retryCount 0; // 重置重试计数 } catch (error) { console.error(监控数据获取失败:, error); this.retryCount; if (this.retryCount this.maxRetries) { this.stop(); this.onCriticalFailure(连续重试失败停止监控); break; } } await this.delay(this.interval); } } async fetchData() { const response await fetch(this.endpoint, { headers: { Cache-Control: no-cache, Pragma: no-cache } }); if (!response.ok) throw new Error(HTTP ${response.status}); const data await response.json(); this.updateDashboard(data); } updateDashboard(data) { // 更新UI组件 this.updateMetric(cpu-usage, data.cpuUsage); this.updateMetric(memory-usage, data.memoryUsage); this.updateMetric(network-io, data.networkIO); // 更新图表数据 this.appendChartData(performance-chart, { timestamp: new Date().toLocaleTimeString(), values: data }); } updateMetric(elementId, value) { const element document.getElementById(elementId); if (element) { element.textContent value; element.className value 80 ? metric-warning : metric-normal; } } onCriticalFailure(message) { alert(监控系统故障: ${message}); } delay(ms) { return new Promise(resolve setTimeout(resolve, ms)); } } // 使用示例 const monitor new RealTimeMonitor(/api/system/metrics); monitor.start(); // 页面卸载时停止监控 window.addEventListener(beforeunload, () { monitor.stop(); });9. 性能优化与最佳实践9.1 请求优化策略class OptimizedApiClient { constructor() { this.cache new Map(); this.pendingRequests new Map(); this.cacheTimeout 300000; // 5分钟缓存 } // 请求去重 async deduplicatedRequest(url, options {}) { const cacheKey this.generateCacheKey(url, options); // 如果已有相同请求在进行中返回同一个 Promise if (this.pendingRequests.has(cacheKey)) { return this.pendingRequests.get(cacheKey); } // 检查缓存 if (options.method GET this.cache.has(cacheKey)) { const cached this.cache.get(cacheKey); if (Date.now() - cached.timestamp this.cacheTimeout) { return Promise.resolve(cached.data); } } const requestPromise this.makeRequest(url, options); this.pendingRequests.set(cacheKey, requestPromise); try { const result await requestPromise; // 缓存 GET 请求结果 if (options.method GET) { this.cache.set(cacheKey, { data: result, timestamp: Date.now() }); } return result; } finally { this.pendingRequests.delete(cacheKey); } } // 批量请求 async batchRequests(requests, concurrency 5) { const results []; const executing new Set(); for (const request of requests) { if (executing.size concurrency) { await Promise.race(executing); } const promise this.deduplicatedRequest(request.url, request.options) .then(result { results.push({ request, result, success: true }); }) .catch(error { results.push({ request, error, success: false }); }) .finally(() { executing.delete(promise); }); executing.add(promise); } await Promise.all(executing); return results; } generateCacheKey(url, options) { return ${url}-${options.method}-${JSON.stringify(options.body)}; } async makeRequest(url, options) { const controller new AbortController(); const timeoutId setTimeout(() controller.abort(), 10000); try { const response await fetch(url, { ...options, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) throw new Error(HTTP ${response.status}); return await response.json(); } catch (error) { clearTimeout(timeoutId); throw error; } } }9.2 监控与调试工具// API 调用监控工具 class ApiMonitor { constructor() { this.metrics { totalRequests: 0, successfulRequests: 0, failedRequests: 0, averageResponseTime: 0 }; this.requestLog []; } wrapFetch() { const originalFetch window.fetch; window.fetch async (...args) { const startTime Date.now(); this.metrics.totalRequests; try { const response await originalFetch(...args); const duration Date.now() - startTime; this.metrics.successfulRequests; this.updateAverageTime(duration); this.logRequest({ url: args[0], method: args[1]?.method || GET, status: response.status, duration: duration, timestamp: new Date().toISOString() }); return response; } catch (error) { this.metrics.failedRequests; this.logRequest({ url: args[0], method: args[1]?.method || GET, status: ERROR, duration: Date.now() - startTime, error: error.message, timestamp: new Date().toISOString() }); throw error; } }; } updateAverageTime(newTime) { this.metrics.averageResponseTime (this.metrics.averageResponseTime * (this.metrics.successfulRequests - 1) newTime) / this.metrics.successfulRequests; } logRequest(requestInfo) { this.requestLog.push(requestInfo); // 保持日志大小可控 if (this.requestLog.length 1000) { this.requestLog this.requestLog.slice(-500); } } getPerformanceReport() { return { ...this.metrics, successRate: (this.metrics.successfulRequests / this.metrics.totalRequests * 100).toFixed(2) %, recentRequests: this.requestLog.slice(-10) }; } } // 使用监控 const monitor new ApiMonitor(); monitor.wrapFetch(); // 在控制台查看性能报告 setInterval(() { console.table(monitor.getPerformanceReport()); }, 30000);10. 常见问题与排查方法问题现象可能原因排查方式解决方案Network Error网络连接问题、CORS 限制检查浏览器网络面板、控制台错误验证网络连接、检查 CORS 配置404 Not FoundURL 错误、资源不存在检查请求 URL、验证接口文档修正 URL、确认接口可用性401 Unauthorized身份验证失败、token 过期检查认证头、验证 token 有效性重新登录、更新 token403 Forbidden权限不足、IP 限制检查用户权限、IP 白名单联系管理员调整权限500 Internal Error服务器端错误查看服务器日志、联系后端等待服务恢复、报告问题请求超时网络延迟、服务器处理慢检查超时设置、网络状况增加超时时间、优化查询解析 JSON 失败响应格式错误、非 JSON 数据检查响应内容类型、实际数据验证接口响应格式内存泄漏未取消请求、事件监听未移除使用 AbortController、清理监听实现正确的资源清理10.1 详细错误排查示例// 综合错误诊断工具 class ApiDiagnostic { static async diagnoseRequest(url, options {}) { const diagnostics { url: url, timestamp: new Date().toISOString(), steps: [] }; try { // 步骤1: 检查 URL 格式 diagnostics.steps.push({ step: URL 验证, status: 检查中 }); if (!this.isValidUrl(url)) { throw new Error(无效的 URL 格式); } diagnostics.steps[0].status 通过; // 步骤2: 网络连通性测试 diagnostics.steps.push({ step: 网络连通性, status: 检查中 }); const networkTest await this.testNetworkConnectivity(url); if (!networkTest.success) { throw new Error(网络不通: ${networkTest.error}); } diagnostics.steps[1].status 通过; diagnostics.networkInfo networkTest; // 步骤3: 实际请求测试 diagnostics.steps.push({ step: API 请求测试, status: 检查中 }); const response await fetch(url, options); diagnostics.steps[2].status 通过; diagnostics.responseInfo { status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()) }; // 步骤4: 响应内容检查 diagnostics.steps.push({ step: 响应内容验证, status: 检查中 }); const content await response.text(); diagnostics.responseInfo.contentType response.headers.get(content-type); diagnostics.responseInfo.contentLength content.length; try { JSON.parse(content); diagnostics.responseInfo.validJson true; } catch { diagnostics.responseInfo.validJson false; } diagnostics.steps[3].status 通过; diagnostics.success true; } catch (error) { diagnostics.error { message: error.message, stack: error.stack }; diagnostics.success false; // 标记失败步骤 const lastStep diagnostics.steps[diagnostics.steps.length - 1]; if (lastStep lastStep.status 检查中) { lastStep.status 失败; lastStep.error error.message; } } return diagnostics; } static isValidUrl(string) { try { new URL(string); return true; } catch { return false; } } static async testNetworkConnectivity(url) { try { const urlObj new URL(url); const testUrl ${urlObj.origin}/; const startTime Date.now(); const response await fetch(testUrl, { method: HEAD, cache: no-cache }); const duration Date.now() - startTime; return { success: true, duration: duration, status: response.status }; } catch (error) { return { success: false, error: error.message }; } } } // 使用诊断工具 ApiDiagnostic.diagnoseRequest(https://jsonplaceholder.typicode.com/posts/1) .then(report { console.log(API 诊断报告:, report); if (!report.success) { console.error(发现问题:, report.error); } });11. 安全最佳实践API 调用中的安全考虑至关重要// 安全增强的 API 客户端 class SecureApiClient { constructor(baseURL) { this.baseURL baseURL; this.csrfToken this.getCSRFToken(); } // CSRF 防护 getCSRFToken() { let token document.querySelector(meta[namecsrf-token])?.getAttribute(content); if (!token) { console.warn(CSRF token 未找到请确保页面中有相关 meta 标签); token ; } return token; } // 安全的请求头设置 getSecureHeaders(additionalHeaders {}) { const baseHeaders { Content-Type: application/json, X-Requested-With: XMLHttpRequest, }; if (this.csrfToken) { baseHeaders[X-CSRF-Token] this.csrfToken; } const authToken localStorage.getItem(authToken); if (authToken) { baseHeaders[Authorization] Bearer ${authToken}; } return { ...baseHeaders, ...additionalHeaders }; } // 输入验证 validateInput(input, schema) { for (const [key, validator] of Object.entries(schema)) { if (validator.required !input[key]) { throw new Error(缺少必要参数: ${key}); } if (input[key] validator.type) { if (typeof input[key] ! validator.type) { throw new Error(参数 ${key} 类型错误期望 ${validator.type}); } } if (validator.validate !validator.validate(input[key])) { throw new Error(参数 ${key} 验证失败); } } } // 安全的 API 调用 async secureFetch(endpoint, options {}) { const url ${this.baseURL}${endpoint}; // 输入验证 if (options.body) { this.validateInput(options.body, this.getValidationSchema(endpoint)); } const config { ...options, headers: this.getSecureHeaders(options.headers), credentials: same-origin // 控制凭证发送 }; try { const response await fetch(url, config); // 检查安全头 const securityHeaders this.checkSecurityHeaders(response); if (!securityHeaders.adequate) { console.warn(安全头不完整:, securityHeaders); } if (!response.ok) { throw new Error(HTTP ${response.status}); } return await response.json(); } catch (error) { this.logSecurityEvent(api_error, { endpoint, error: error.message }); throw error; } } checkSecurityHeaders(response) { const headers response.headers; return { adequate: headers.has(x-content-type-options) headers.has(x-frame-options), missing: [ !headers.has(x-content-type-options) x-content-type-options, !headers.has(x-frame-options) x-frame-options, !headers.has(strict-transport-security) hsts