【axios进阶实战】从零搭建Mock API到封装企业级请求库

📅 2026/6/28 23:00:24
【axios进阶实战】从零搭建Mock API到封装企业级请求库
1. 从零搭建Mock APIjson-server实战指南在前后端分离的开发模式下前端经常需要模拟后端API进行开发和测试。json-server就是一个能够快速搭建REST API的神器它可以在30秒内帮你创建一个完整的模拟后端服务。1.1 为什么需要Mock API在实际项目开发中前后端开发往往是并行进行的。如果前端必须等待后端接口开发完成才能开始工作会严重影响开发效率。Mock API的出现解决了这个痛点它允许前端开发者在没有真实后端的情况下独立进行功能开发模拟各种网络请求场景成功、失败、超时等快速验证前端逻辑的正确性提前发现接口设计可能存在的问题我曾在多个项目中实践过使用Mock API至少能提升40%的前端开发效率。特别是在敏捷开发中当后端API还在设计阶段时前端就可以基于Mock API开始工作了。1.2 json-server的核心优势相比其他Mock工具json-server有几个不可替代的优势零配置启动只需一个JSON文件就能创建完整的REST API支持完整的RESTful规范自动支持GET、POST、PUT、PATCH、DELETE等HTTP方法丰富的查询功能内置_filter、_sort、_page等实用参数关系型数据支持可以通过外键建立数据关联中间件扩展支持通过中间件添加自定义路由和逻辑1.3 快速上手json-server让我们通过一个完整的例子来体验json-server的强大功能。首先确保你已经安装了Node.js环境然后执行以下命令全局安装json-servernpm install -g json-server接下来在项目目录下创建一个db.json文件这就是我们的数据库{ posts: [ { id: 1, title: json-server入门, author: 张三 }, { id: 2, title: axios进阶指南, author: 李四 } ], comments: [ { id: 1, body: 很有帮助, postId: 1 } ], profile: { name: 技术博客 } }启动服务只需要一行命令json-server --watch db.json --port 3000现在你已经拥有了一个完整的REST API服务支持以下端点GET /posts - 获取所有文章GET /posts/1 - 获取ID为1的文章POST /posts - 创建新文章PUT /posts/1 - 替换ID为1的文章PATCH /posts/1 - 部分更新ID为1的文章DELETE /posts/1 - 删除ID为1的文章1.4 高级功能探索json-server还提供了一些非常实用的高级功能数据关联查询GET /posts/1?_embedcomments # 获取文章及其评论 GET /comments?_expandpost # 获取评论并展开关联文章分页与排序GET /posts?_page2_limit5 # 获取第2页每页5条 GET /posts?_sortviews_orderdesc # 按浏览量降序条件过滤GET /posts?title_likeaxios # 标题包含axios GET /posts?id_ne1 # ID不等于1 GET /posts?views_gte100 # 浏览量大于等于100在实际项目中我经常使用这些功能来模拟各种复杂的数据查询场景。比如测试前端的分页组件时_page和_limit参数就非常有用。2. 手动封装简易版axios理解请求库核心原理理解了Mock API的工作原理后我们来深入HTTP请求库的实现原理。通过手动封装一个简易版axios可以更好地理解现代请求库的设计思想。2.1 设计我们的请求库一个基础的HTTP请求库应该具备以下能力支持常见的HTTP方法GET、POST等能够处理查询参数和请求体返回Promise以便异步处理自动转换JSON数据统一的错误处理机制基于这些需求我们的设计目标是创建一个名为miniAxios的函数它接收一个配置对象返回一个Promise。2.2 核心实现代码下面是miniAxios的核心实现我添加了详细的注释说明每个关键部分function miniAxios(config) { return new Promise((resolve, reject) { // 解构配置参数设置默认值 const { url, method GET, params {}, data {}, headers {} } config // 创建XMLHttpRequest实例 const xhr new XMLHttpRequest() // 处理查询参数将{key: value}转换为keyvalue的形式 const queryString Object.keys(params) .map(key ${encodeURIComponent(key)}${encodeURIComponent(params[key])}) .join() // 构建完整URL const requestUrl queryString ? ${url}?${queryString} : url // 初始化请求 xhr.open(method.toUpperCase(), requestUrl, true) // 设置请求头 Object.keys(headers).forEach(key { xhr.setRequestHeader(key, headers[key]) }) // 处理不同请求方法的发送数据 let requestData null if ([POST, PUT, PATCH].includes(method.toUpperCase())) { // 如果是JSON数据 if (headers[Content-Type] application/json) { requestData JSON.stringify(data) } else { // 表单格式数据 requestData new URLSearchParams() Object.keys(data).forEach(key { requestData.append(key, data[key]) }) } } // 发送请求 xhr.send(requestData) // 处理响应 xhr.onreadystatechange function() { if (xhr.readyState ! 4) return if (xhr.status 200 xhr.status 300) { // 成功响应 const response { data: tryParseJSON(xhr.responseText), status: xhr.status, statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders()), config: config, request: xhr } resolve(response) } else { // 错误处理 const error new Error(Request failed with status code ${xhr.status}) error.config config error.request xhr error.response { status: xhr.status, statusText: xhr.statusText, data: tryParseJSON(xhr.responseText) } reject(error) } } // 处理网络错误 xhr.onerror function() { reject(new Error(Network Error)) } // 超时处理 xhr.ontimeout function() { reject(new Error(Timeout of ${config.timeout}ms exceeded)) } }) } // 辅助函数尝试解析JSON function tryParseJSON(jsonString) { try { return JSON.parse(jsonString) } catch (e) { return jsonString } } // 辅助函数解析响应头 function parseHeaders(headersString) { const headers {} headersString.split(\r\n).forEach(line { if (line) { const [key, value] line.split(: ) headers[key.toLowerCase()] value } }) return headers }2.3 使用示例与测试现在我们可以像使用axios一样使用我们的miniAxios了// GET请求示例 miniAxios({ url: http://localhost:3000/posts, params: { id: 1 } }).then(response { console.log(GET响应:, response.data) }).catch(error { console.error(GET错误:, error) }) // POST请求示例 miniAxios({ url: http://localhost:3000/posts, method: POST, data: { title: 新文章, author: 王五 }, headers: { Content-Type: application/json } }).then(response { console.log(POST响应:, response.data) }).catch(error { console.error(POST错误:, error) })在实际项目中封装这样一个请求库时有几个关键点需要注意编码安全性使用encodeURIComponent处理参数值防止XSS攻击错误处理区分网络错误、HTTP错误和业务错误性能优化合理设置默认超时时间避免请求长时间挂起类型判断正确处理各种格式的请求数据和响应数据3. axios高级应用企业级请求库封装理解了请求库的核心原理后我们来看看如何在企业级项目中使用axios。axios提供了许多高级功能合理使用这些功能可以显著提升应用的质量和开发效率。3.1 创建多个axios实例在实际项目中我们经常需要对接多个后端服务每个服务可能有不同的基础URL、超时设置或认证方式。这时创建多个axios实例就非常有用。// 主服务实例 const mainService axios.create({ baseURL: https://api.example.com/v1, timeout: 5000, headers: { X-Custom-Header: foobar } }) // 认证服务实例 const authService axios.create({ baseURL: https://auth.example.com, timeout: 3000 }) // 文件服务实例 const fileService axios.create({ baseURL: https://storage.example.com, timeout: 10000 })我在一个电商项目中实践过这种模式我们将商品服务、订单服务、用户服务分别封装成不同的axios实例这样不仅代码更清晰而且当某个服务的配置需要调整时只需修改对应的实例配置不会影响其他服务。3.2 拦截器的实战应用拦截器是axios最强大的功能之一它允许我们在请求发出前和响应返回后插入处理逻辑。下面分享几个我在实际项目中常用的拦截器模式。请求拦截器示例// 添加请求拦截器 axios.interceptors.request.use( config { // 在发送请求前做些什么 const token localStorage.getItem(authToken) if (token) { config.headers.Authorization Bearer ${token} } // 记录请求开始时间 config.metadata { startTime: new Date() } return config }, error { // 对请求错误做些什么 return Promise.reject(error) } )响应拦截器示例// 添加响应拦截器 axios.interceptors.response.use( response { // 对响应数据做点什么 const endTime new Date() const duration endTime - response.config.metadata.startTime console.log(请求 ${response.config.url} 耗时 ${duration}ms) // 处理业务错误码 if (response.data.code ! 0) { return Promise.reject(response.data) } return response.data.data }, error { // 对响应错误做点什么 if (error.response) { switch (error.response.status) { case 401: // 处理未授权 break case 403: // 处理禁止访问 break case 500: // 处理服务器错误 break } } return Promise.reject(error) } )实际项目经验性能监控通过拦截器记录请求耗时找出性能瓶颈统一错误处理将各种HTTP错误和业务错误统一转换为标准格式重试机制对于特定错误如网络超时可以自动重试请求取消在拦截器中实现自动取消重复请求的功能3.3 请求取消的深度实践在复杂的前端应用中请求取消是一个非常重要但常被忽视的功能。它能有效解决以下问题组件卸载后避免setState警告取消重复的请求实现请求竞速race conditions基本取消示例const CancelToken axios.CancelToken let cancel axios.get(/api/data, { cancelToken: new CancelToken(function executor(c) { // executor函数接收一个cancel函数作为参数 cancel c }) }) // 取消请求 cancel(Operation canceled by the user.)自动取消重复请求const pendingRequests new Map() axios.interceptors.request.use(config { const requestKey ${config.method}-${config.url} // 如果存在相同请求则取消前一个 if (pendingRequests.has(requestKey)) { pendingRequests.get(requestKey)(取消重复请求) } // 创建新的CancelToken config.cancelToken new axios.CancelToken(cancel { pendingRequests.set(requestKey, cancel) }) return config }) axios.interceptors.response.use( response { const requestKey ${response.config.method}-${response.config.url} pendingRequests.delete(requestKey) return response }, error { if (axios.isCancel(error)) { console.log(请求被取消:, error.message) return Promise.reject(error) } return Promise.reject(error) } )在实际项目中我使用这种模式成功解决了表单重复提交和页面快速切换导致的请求堆积问题。特别是在搜索场景下当用户快速输入关键词时可以确保只处理最后一次请求的结果。4. 企业级请求库的最佳实践基于前面的知识我们现在可以探讨如何封装一个真正企业级的请求库。这些经验都来自我在多个大型项目中的实践总结。4.1 配置管理策略良好的配置管理是请求库稳定的基础。我推荐采用三级配置策略全局默认配置适用于所有请求的基本配置实例特定配置针对特定服务的配置请求特定配置单个请求的特殊配置// 全局默认配置 axios.defaults.baseURL https://api.example.com axios.defaults.timeout 10000 axios.defaults.headers.common[Accept] application/json // 实例特定配置 const authInstance axios.create({ baseURL: https://auth.example.com, timeout: 5000 }) // 请求特定配置 authInstance.post(/login, { username: admin, password: password }, { timeout: 15000 // 这个登录请求需要更长超时时间 })4.2 错误处理的标准化统一的错误处理可以极大提升代码的可维护性。我建议将错误分为以下几类网络错误请求未能到达服务器HTTP错误服务器返回了错误状态码业务错误服务器返回了成功状态码但业务逻辑失败取消错误请求被主动取消// 错误处理中间件 function errorHandler(error) { if (axios.isCancel(error)) { // 请求取消处理 console.log(请求取消:, error.message) return } if (!error.response) { // 网络错误 console.error(网络错误:, error.message) return } const { status, data } error.response if (status 500) { // 服务器错误 console.error(服务器错误:, status) } else if (status 401) { // 未授权 console.error(未授权请重新登录) } else if (status 403) { // 禁止访问 console.error(无权访问该资源) } else if (status 400) { // 请求参数错误 console.error(参数错误:, data.message) } else if (data data.code) { // 业务错误 console.error(业务错误:, data.message) } // 可以在这里添加错误上报逻辑 reportError(error) } // 使用示例 axios.get(/api/data) .then(response { // 处理成功响应 }) .catch(errorHandler)4.3 性能优化技巧过多个项目的优化实践我总结出以下几点请求库性能优化经验合理设置超时时间普通API请求5-10秒文件上传/下载30-60秒特殊长任务按需设置连接复用// 启用keep-alive const http require(http) const https require(https) const httpAgent new http.Agent({ keepAlive: true }) const httpsAgent new https.Agent({ keepAlive: true }) axios.defaults.httpAgent httpAgent axios.defaults.httpsAgent httpsAgent请求缓存 对于不常变化的数据可以实现简单的内存缓存const cache new Map() function cachedRequest(config) { const cacheKey JSON.stringify(config) if (cache.has(cacheKey)) { return Promise.resolve(cache.get(cacheKey)) } return axios(config).then(response { cache.set(cacheKey, response.data) return response.data }) }请求合并 对于短时间内相同的多个请求可以合并为一个const pendingPromises new Map() function dedupeRequest(config) { const requestKey ${config.method}-${config.url}-${JSON.stringify(config.params)} if (pendingPromises.has(requestKey)) { return pendingPromises.get(requestKey) } const promise axios(config).finally(() { pendingPromises.delete(requestKey) }) pendingPromises.set(requestKey, promise) return promise }4.4 安全防护措施在企业级应用中请求库的安全防护至关重要。以下是我在实践中总结的几个关键点CSRF防护// 从cookie中获取token function getCookie(name) { const value ; ${document.cookie} const parts value.split(; ${name}) if (parts.length 2) return parts.pop().split(;).shift() } // 添加CSRF Token axios.interceptors.request.use(config { config.headers[X-CSRF-Token] getCookie(csrfToken) return config })请求签名 对于敏感操作可以实现请求签名防止篡改axios.interceptors.request.use(config { const timestamp Date.now() const nonce Math.random().toString(36).substring(2) const sign generateSign(config, timestamp, nonce) config.headers[X-Timestamp] timestamp config.headers[X-Nonce] nonce config.headers[X-Signature] sign return config })敏感数据过滤 在开发环境中可以添加拦截器过滤日志中的敏感信息if (process.env.NODE_ENV development) { axios.interceptors.response.use(response { const safeData filterSensitiveData(response.data) console.log(响应数据:, safeData) return response }) }这些实践经验都来自真实的企业级项目经过大量用户验证能够有效提升应用的安全性和稳定性。在实际应用中还需要根据具体业务需求进行调整和扩展。