网络请求问题
本页面收集了 uni-app 网络请求相关的常见问题和解决方案。
基础请求问题
Q: 请求无法发送或超时
问题描述:网络请求无法发送或经常超时。
解决方案:
- 检查网络连接是否正常
- 检查请求 URL 是否正确,特别是协议部分(http/https)
- 增加请求超时时间:js
uni.request({ url: 'https://api.example.com/data', timeout: 10000, // 设置更长的超时时间(10秒) success: (res) => { console.log(res.data); }, fail: (err) => { console.error('请求失败', err); } });
- 实现请求重试机制:js
function requestWithRetry(options, maxRetries = 3) { let retryCount = 0; function sendRequest() { uni.request({ ...options, fail: (err) => { if (retryCount < maxRetries) { retryCount++; console.log(`请求失败,第${retryCount}次重试`); setTimeout(sendRequest, 1000 * retryCount); } else if (options.fail) { options.fail(err); } } }); } sendRequest(); }
Q: 请求返回 404、500 等错误
问题描述:请求返回 HTTP 错误状态码。
解决方案:
- 检查请求 URL 是否正确
- 检查服务器是否正常运行
- 检查请求参数是否符合接口要求
- 实现错误处理逻辑:js
uni.request({ url: 'https://api.example.com/data', success: (res) => { if (res.statusCode === 200) { // 请求成功 console.log(res.data); } else { // 处理不同的错误状态码 switch (res.statusCode) { case 404: console.error('资源不存在'); break; case 401: console.error('未授权,请登录'); // 跳转到登录页 uni.navigateTo({ url: '/pages/login/login' }); break; case 500: console.error('服务器内部错误'); break; default: console.error(`请求错误: ${res.statusCode}`); } } }, fail: (err) => { console.error('请求失败', err); } });
跨域问题
Q: H5 端请求跨域问题
问题描述:在 H5 端发起请求时遇到跨域限制。
解决方案:
- 在服务端设置 CORS 头部:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization
- 在 manifest.json 中配置代理:json
{ "h5": { "devServer": { "port": 8080, "disableHostCheck": true, "proxy": { "/api": { "target": "https://api.example.com", "changeOrigin": true, "pathRewrite": { "^/api": "" } } } } } }
- 使用 JSONP 方式请求(仅适用于 GET 请求)
- 使用服务端中转请求
Q: 小程序端请求域名限制
问题描述:小程序请求提示域名未授权。
解决方案:
- 在小程序管理后台添加服务器域名到白名单
- 开发阶段可以在开发者工具中勾选"不校验合法域名"
- 使用小程序云函数中转请求
- 确保请求的 URL 使用 HTTPS 协议
数据格式问题
Q: 请求数据格式错误
问题描述:服务器无法正确解析请求数据。
解决方案:
- 检查请求头 Content-Type 是否正确设置:js
uni.request({ url: 'https://api.example.com/data', method: 'POST', header: { 'Content-Type': 'application/json' }, data: JSON.stringify(postData), success: (res) => { console.log(res.data); } });
- 确保数据格式符合接口要求
- 检查是否需要对特殊字符进行编码
- 使用 uni.request 的 dataType 参数指定响应数据类型
Q: 响应数据解析错误
问题描述:无法正确解析服务器返回的数据。
解决方案:
- 检查响应数据格式是否符合预期
- 使用 dataType 参数指定响应数据类型:js
uni.request({ url: 'https://api.example.com/data', dataType: 'json', // 指定响应数据为 JSON 格式 success: (res) => { console.log(res.data); } });
- 手动解析响应数据:js
uni.request({ url: 'https://api.example.com/data', success: (res) => { try { const data = JSON.parse(res.data); console.log(data); } catch (e) { console.error('数据解析错误', e); } } });
- 检查服务器返回的数据编码是否正确
授权和认证问题
Q: 请求需要携带认证信息
问题描述:请求需要携带 token 或其他认证信息。
解决方案:
- 在请求头中添加认证信息:js
const token = uni.getStorageSync('token'); uni.request({ url: 'https://api.example.com/data', header: { 'Authorization': `Bearer ${token}` }, success: (res) => { console.log(res.data); } });
- 实现请求拦截器统一添加认证信息:js
// request.js const request = (options) => { // 克隆原始选项 const requestOptions = { ...options }; // 添加基础 URL requestOptions.url = baseUrl + requestOptions.url; // 添加请求头 requestOptions.header = { ...requestOptions.header, 'Authorization': `Bearer ${uni.getStorageSync('token')}` }; // 发送请求 return new Promise((resolve, reject) => { uni.request({ ...requestOptions, success: (res) => { if (res.statusCode === 200) { resolve(res.data); } else if (res.statusCode === 401) { // 处理认证失败 uni.navigateTo({ url: '/pages/login/login' }); reject(new Error('认证失败')); } else { reject(new Error(`请求失败: ${res.statusCode}`)); } }, fail: (err) => { reject(err); } }); }); };
- 使用 cookie 存储会话信息(需要服务端支持)
- 实现 token 刷新机制处理过期问题
Q: 登录状态失效问题
问题描述:用户登录状态失效,需要重新登录。
解决方案:
- 实现 token 过期检测和自动刷新:js
// 检查 token 是否过期 function isTokenExpired(token) { if (!token) return true; try { // 假设 token 是 JWT 格式 const payload = JSON.parse(atob(token.split('.')[1])); return payload.exp < Date.now() / 1000; } catch (e) { return true; } } // 刷新 token async function refreshToken() { try { const refreshToken = uni.getStorageSync('refreshToken'); const res = await uni.request({ url: 'https://api.example.com/refresh', method: 'POST', data: { refreshToken } }); if (res.statusCode === 200) { uni.setStorageSync('token', res.data.token); uni.setStorageSync('refreshToken', res.data.refreshToken); return res.data.token; } else { throw new Error('刷新 token 失败'); } } catch (e) { // 刷新失败,需要重新登录 uni.removeStorageSync('token'); uni.removeStorageSync('refreshToken'); uni.navigateTo({ url: '/pages/login/login' }); throw e; } }
- 统一处理 401 错误,自动跳转到登录页
- 实现会话保持机制,定期刷新 token
- 使用本地存储保存用户信息,减少重复登录
请求管理和优化
Q: 如何取消正在进行的请求
问题描述:需要取消已发出但尚未完成的请求。
解决方案:
- 使用 requestTask 对象取消请求:js
const requestTask = uni.request({ url: 'https://api.example.com/data', success: (res) => { console.log(res.data); } }); // 取消请求 requestTask.abort();
- 实现请求管理器,跟踪和取消请求:js
class RequestManager { constructor() { this.requests = new Map(); } // 添加请求 addRequest(key, requestTask) { this.requests.set(key, requestTask); } // 移除请求 removeRequest(key) { this.requests.delete(key); } // 取消请求 cancelRequest(key) { const requestTask = this.requests.get(key); if (requestTask) { requestTask.abort(); this.removeRequest(key); } } // 取消所有请求 cancelAllRequests() { this.requests.forEach(requestTask => { requestTask.abort(); }); this.requests.clear(); } } // 使用请求管理器 const requestManager = new RequestManager(); function sendRequest(url, key) { const requestTask = uni.request({ url, complete: () => { requestManager.removeRequest(key); } }); requestManager.addRequest(key, requestTask); return requestTask; } // 页面卸载时取消所有请求 onUnload() { requestManager.cancelAllRequests(); }
- 在页面切换或组件卸载时主动取消请求
- 使用超时机制自动取消长时间未响应的请求
Q: 如何处理并发请求限制
问题描述:同时发起大量请求导致性能问题或被服务器限流。
解决方案:
- 实现请求队列,控制并发数量:js
class RequestQueue { constructor(maxConcurrent = 5) { this.queue = []; this.running = 0; this.maxConcurrent = maxConcurrent; } add(requestFn) { return new Promise((resolve, reject) => { this.queue.push({ requestFn, resolve, reject }); this.run(); }); } run() { if (this.running >= this.maxConcurrent || this.queue.length === 0) { return; } const { requestFn, resolve, reject } = this.queue.shift(); this.running++; requestFn() .then(resolve) .catch(reject) .finally(() => { this.running--; this.run(); }); } } // 使用请求队列 const requestQueue = new RequestQueue(3); function sendRequest(url) { return requestQueue.add(() => { return new Promise((resolve, reject) => { uni.request({ url, success: resolve, fail: reject }); }); }); }
- 合并请求,减少请求次数
- 使用批量接口一次获取多条数据
- 实现请求节流,避免短时间内重复请求同一资源
网络状态管理
Q: 如何处理弱网或离线情况
问题描述:在网络不稳定或离线情况下,请求失败影响用户体验。
解决方案:
- 监听网络状态变化:js
// 监听网络状态 uni.onNetworkStatusChange(function(res) { console.log(`网络类型: ${res.networkType}`); console.log(`是否已连接: ${res.isConnected}`); if (res.isConnected) { // 网络恢复,可以重新发送失败的请求 resendFailedRequests(); } else { // 网络断开,提示用户 uni.showToast({ title: '网络连接已断开', icon: 'none' }); } }); // 获取当前网络状态 uni.getNetworkType({ success: (res) => { console.log(`当前网络类型: ${res.networkType}`); } });
- 实现离线数据缓存和同步:js
// 带离线缓存的请求函数 async function requestWithCache(url, options = {}) { const cacheKey = `cache_${url}`; try { // 检查网络状态 const networkStatus = await getNetworkStatus(); if (networkStatus.isConnected) { // 有网络,发送请求 const response = await sendRequest(url, options); // 缓存响应数据 uni.setStorageSync(cacheKey, { data: response.data, timestamp: Date.now() }); return response.data; } else { // 无网络,使用缓存 const cache = uni.getStorageSync(cacheKey); if (cache) { console.log(`使用缓存数据: ${url}`); return cache.data; } else { throw new Error('无网络连接且无缓存数据'); } } } catch (error) { // 请求失败,尝试使用缓存 const cache = uni.getStorageSync(cacheKey); if (cache) { console.log(`请求失败,使用缓存数据: ${url}`); return cache.data; } else { throw error; } } }
- 提供离线模式,允许用户在无网络时浏览缓存内容
- 实现请求队列,在网络恢复时自动重发失败的请求
Q: 如何优化弱网环境下的用户体验
问题描述:在网络不稳定的环境下,请求缓慢影响用户体验。
解决方案:
- 实现渐进式加载,先显示骨架屏或占位内容
- 优先加载关键数据,延迟加载非关键内容
- 减小请求数据量,使用分页或按需加载
- 提供加载状态反馈:js
// 带加载状态的请求函数 async function requestWithLoading(url, options = {}) { const showLoading = options.showLoading !== false; if (showLoading) { uni.showLoading({ title: options.loadingText || '加载中...', mask: options.loadingMask || false }); } try { const response = await sendRequest(url, options); return response; } finally { if (showLoading) { uni.hideLoading(); } } }
文件上传和下载
Q: 文件上传失败或中断
问题描述:上传文件时失败或中断。
解决方案:
- 使用分片上传大文件:js
// 分片上传文件 async function uploadFileInChunks(filePath, options = {}) { const chunkSize = options.chunkSize || 1024 * 1024; // 默认 1MB 一片 const fileInfo = await getFileInfo(filePath); const totalSize = fileInfo.size; const chunks = Math.ceil(totalSize / chunkSize); // 创建上传任务 const uploadId = await createUploadTask(fileInfo.name, totalSize); // 上传分片 for (let i = 0; i < chunks; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, totalSize); await uploadChunk({ filePath, uploadId, chunkIndex: i, start, end }); // 更新进度 if (options.onProgress) { options.onProgress({ progress: Math.floor((i + 1) / chunks * 100), totalChunks: chunks, currentChunk: i + 1 }); } } // 完成上传 return await completeUpload(uploadId); }
- 实现断点续传:js
// 断点续传 async function resumableUpload(filePath, options = {}) { const fileInfo = await getFileInfo(filePath); const uploadKey = `upload_${fileInfo.name}_${fileInfo.size}`; // 尝试获取上传进度 let uploadProgress = uni.getStorageSync(uploadKey) || { uploadId: null, uploadedChunks: [] }; // 如果没有上传ID或上传已过期,创建新的上传任务 if (!uploadProgress.uploadId) { uploadProgress.uploadId = await createUploadTask(fileInfo.name, fileInfo.size); uploadProgress.uploadedChunks = []; } // 计算剩余需要上传的分片 const chunkSize = options.chunkSize || 1024 * 1024; const totalChunks = Math.ceil(fileInfo.size / chunkSize); const remainingChunks = []; for (let i = 0; i < totalChunks; i++) { if (!uploadProgress.uploadedChunks.includes(i)) { remainingChunks.push(i); } } // 上传剩余分片 for (const chunkIndex of remainingChunks) { const start = chunkIndex * chunkSize; const end = Math.min(start + chunkSize, fileInfo.size); await uploadChunk({ filePath, uploadId: uploadProgress.uploadId, chunkIndex, start, end }); // 更新已上传分片记录 uploadProgress.uploadedChunks.push(chunkIndex); uni.setStorageSync(uploadKey, uploadProgress); // 更新进度 if (options.onProgress) { options.onProgress({ progress: Math.floor(uploadProgress.uploadedChunks.length / totalChunks * 100), totalChunks, uploadedChunks: uploadProgress.uploadedChunks.length }); } } // 完成上传 const result = await completeUpload(uploadProgress.uploadId); // 清理上传记录 uni.removeStorageSync(uploadKey); return result; }
- 添加重试机制,自动重试失败的上传
- 优化上传参数,如超时时间和并发数
Q: 文件下载问题
问题描述:文件下载失败或无法打开下载的文件。
解决方案:
- 使用 uni.downloadFile API 下载文件:js
// 下载文件并显示进度 function downloadFile(url, options = {}) { const downloadTask = uni.downloadFile({ url, header: options.header, timeout: options.timeout || 60000, success: (res) => { if (res.statusCode === 200) { console.log('下载成功', res.tempFilePath); if (options.saveFile) { // 保存文件到本地 uni.saveFile({ tempFilePath: res.tempFilePath, success: (saveRes) => { console.log('文件已保存', saveRes.savedFilePath); if (options.success) options.success(saveRes.savedFilePath); }, fail: (err) => { console.error('保存文件失败', err); if (options.fail) options.fail(err); } }); } else if (options.success) { options.success(res.tempFilePath); } } else { console.error('下载失败', res); if (options.fail) options.fail(new Error(`下载失败: ${res.statusCode}`)); } }, fail: (err) => { console.error('下载失败', err); if (options.fail) options.fail(err); } }); // 监听下载进度 if (options.onProgress) { downloadTask.onProgressUpdate((res) => { options.onProgress({ progress: res.progress, totalBytesWritten: res.totalBytesWritten, totalBytesExpectedToWrite: res.totalBytesExpectedToWrite }); }); } return downloadTask; }
- 检查文件保存路径和权限
- 实现大文件分片下载
- 添加文件完整性校验,如 MD5 校验
最佳实践
请求封装和统一处理
封装请求函数,统一处理错误和响应:
js// request.js // 基础配置 const config = { baseUrl: 'https://api.example.com', timeout: 10000, header: { 'Content-Type': 'application/json' } }; // 请求拦截器 const requestInterceptors = []; // 响应拦截器 const responseInterceptors = []; // 添加请求拦截器 function addRequestInterceptor(interceptor) { requestInterceptors.push(interceptor); } // 添加响应拦截器 function addResponseInterceptor(interceptor) { responseInterceptors.push(interceptor); } // 处理请求配置 function processRequestConfig(options) { let processedOptions = { ...config, ...options }; // 应用请求拦截器 for (const interceptor of requestInterceptors) { processedOptions = interceptor(processedOptions); } return processedOptions; } // 处理响应数据 function processResponse(response, options) { let processedResponse = response; // 应用响应拦截器 for (const interceptor of responseInterceptors) { processedResponse = interceptor(processedResponse, options); } return processedResponse; } // 请求函数 function request(options) { const processedOptions = processRequestConfig(options); return new Promise((resolve, reject) => { uni.request({ ...processedOptions, success: (res) => { const processedResponse = processResponse(res, processedOptions); if (processedResponse.statusCode >= 200 && processedResponse.statusCode < 300) { resolve(processedResponse.data); } else { reject(processedResponse); } }, fail: (err) => { reject(err); } }); }); } // 导出请求方法 export default { request, get: (url, data, options = {}) => request({ url, data, method: 'GET', ...options }), post: (url, data, options = {}) => request({ url, data, method: 'POST', ...options }), put: (url, data, options = {}) => request({ url, data, method: 'PUT', ...options }), delete: (url, data, options = {}) => request({ url, data, method: 'DELETE', ...options }), addRequestInterceptor, addResponseInterceptor };
添加全局错误处理:
js// 添加全局错误处理拦截器 addResponseInterceptor((response) => { if (response.statusCode === 401) { // 处理未授权错误 uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' }); // 跳转到登录页 setTimeout(() => { uni.navigateTo({ url: '/pages/login/login' }); }, 1500); } else if (response.statusCode >= 500) { // 处理服务器错误 uni.showToast({ title: '服务器错误,请稍后再试', icon: 'none' }); } return response; });
数据缓存策略
实现数据缓存和过期控制:
js// cache.js // 缓存数据到本地存储 function setCache(key, data, expireTime = 3600) { uni.setStorageSync(key, { data, expireAt: Date.now() + expireTime * 1000 }); } // 获取缓存数据 function getCache(key) { const cache = uni.getStorageSync(key); if (!cache) return null; // 检查是否过期 if (cache.expireAt && cache.expireAt < Date.now()) { uni.removeStorageSync(key); return null; } return cache.data; } // 带缓存的请求函数 async function requestWithCache(url, options = {}) { const cacheKey = `cache_${url}_${JSON.stringify(options.data || {})}`; const useCache = options.useCache !== false; const cacheTime = options.cacheTime || 300; // 默认缓存5分钟 // 如果启用缓存,尝试从缓存获取 if (useCache) { const cachedData = getCache(cacheKey); if (cachedData) { return cachedData; } } // 发送请求 const response = await request({ url, ...options }); // 缓存响应数据 if (useCache) { setCache(cacheKey, response, cacheTime); } return response; }
实现数据预加载:
js// 预加载常用数据 function preloadCommonData() { // 预加载用户信息 requestWithCache('/api/user/info', { cacheTime: 3600 }); // 预加载应用配置 requestWithCache('/api/config', { cacheTime: 86400 }); // 预加载其他常用数据 // ... } // 在应用启动时调用 // App.vue onLaunch() { preloadCommonData(); }
如果您遇到的问题在本页面没有找到解决方案,请查看 uni-app 官方文档 或在 uni-app 社区 中寻求帮助。
常见错误代码解析
网络请求错误码
错误码 | 说明 | 可能原因 | 解决方案 |
---|---|---|---|
-1 | 未知错误 | 网络异常、请求超时等 | 检查网络连接,增加超时时间 |
401 | 未授权 | 用户未登录或 token 已过期 | 重新登录获取新 token |
403 | 禁止访问 | 用户无权限访问该资源 | 检查用户权限设置 |
404 | 资源不存在 | URL 错误或资源已删除 | 检查请求 URL 是否正确 |
500 | 服务器内部错误 | 服务端代码异常 | 联系后端开发人员修复 |
502 | 网关错误 | 服务器重启或负载均衡问题 | 稍后重试或联系服务提供商 |
503 | 服务不可用 | 服务器维护或过载 | 稍后重试 |
小程序请求特有错误
错误码 | 说明 | 可能原因 | 解决方案 |
---|---|---|---|
12000 | 网络出错 | 网络连接失败 | 检查网络连接 |
12001 | 请求超时 | 服务器响应时间过长 | 增加超时时间或优化服务器响应速度 |
12002 | 请求被拦截 | 域名未配置或 HTTPS 证书无效 | 检查域名配置和 HTTPS 证书 |
12003 | 请求资源不存在 | URL 错误 | 检查请求 URL |
12006 | 请求被中断 | 调用了 abort 方法或网络断开 | 检查是否有代码主动中断请求 |
调试技巧
网络请求调试
使用控制台查看请求:
- 在 HBuilderX 中使用 console.log 打印请求参数和响应结果
- 在微信开发者工具中使用 Network 面板查看请求详情
添加调试日志:
js// 添加请求日志拦截器 addRequestInterceptor((options) => { console.log('请求参数:', options); return options; }); // 添加响应日志拦截器 addResponseInterceptor((response, options) => { console.log(`请求 ${options.url} 响应:`, response); return response; });
使用抓包工具:
- 使用 Charles、Fiddler 等工具抓包分析请求和响应
- 在 App 端可以使用代理方式进行抓包
常见问题排查流程
请求无法发送:
- 检查网络连接
- 检查请求 URL 是否正确
- 检查小程序域名是否已配置
- 检查 HTTPS 证书是否有效
请求发送但无响应:
- 检查请求是否超时
- 检查服务器是否正常运行
- 检查防火墙或安全策略是否拦截
请求返回错误:
- 检查请求参数是否正确
- 检查认证信息是否有效
- 查看服务器日志了解具体错误
数据解析错误:
- 检查响应数据格式
- 检查 dataType 参数是否正确
- 检查是否有特殊字符导致解析失败
性能优化建议
减少请求次数:
- 合并多个请求
- 使用批量接口
- 实现数据缓存策略
减小请求数据量:
- 只请求必要的数据
- 使用分页加载
- 压缩请求和响应数据
优化请求时机:
- 避免重复请求
- 实现请求节流和防抖
- 预加载可能需要的数据
优化错误处理:
- 实现优雅的降级策略
- 提供友好的错误提示
- 自动重试非关键请求