uni-app 网络之封装实战HTTP请求框架

前言

在uniapp开发中,网络请求是每个应用都必不可少的功能模块。一个优秀的网络请求封装不仅能提高开发效率,还能增强代码的可维护性和可扩展性。本文将基于实际项目经验,详细介绍如何封装一个高效、可维护的Uniapp网络请求框架,并结合必应图片API的调用示例,展示其完整使用流程。

一、网络请求封装的核心目标

  1. 统一管理:集中管理请求配置、拦截器等
  2. 复用性:减少重复代码,提高开发效率
  3. 可扩展性:方便添加新功能如日志记录、错误处理等
  4. 可维护性:清晰的代码结构,便于团队协作

二、封装架构设计

1.项目结构规划

项目根目录下新建文件夹common,在其目录下新建 api 文件夹以及 vmeitime-http 文件夹 ,如下

common/
├── api/               # API接口管理
│   └── index.js       # 接口统一出口
└── vmeitime-http/     # HTTP核心封装├── http.js        # 请求核心实现└── interface.js   # 基础请求方法

然后再项目根目录下的main.js下引用

//api
import api from '@/common/api/index.js'
Vue.prototype.$api = api

2.核心组件解析

(1) 基础请求层 (interface.js)

这是整个框架的基石,实现了:

  • 基础请求方法(GET/POST/PUT/DELETE)
  • 请求超时处理
  • 请求ID生成(便于日志追踪)
  • 基础配置管理
/*** 通用uni-app网络请求* 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截*/
export default {config: {baseUrl: "",header: {'Content-Type': 'application/x-www-form-urlencoded'},data: {},method: "POST",dataType: "json",/* 如设为json,会对返回的数据做一次 JSON.parse */responseType: "text",timeout: 15000, // 全局超时时间 15 秒fail() {},complete() {}},interceptor: {request: null,response: null},request(options) {if (!options) {options = {}}// 1. 生成唯一请求ID并挂载到optionsconst requestId = generateRequestId();options.requestId = requestId; // 给请求配置添加IDoptions.baseUrl = options.baseUrl || this.config.baseUrloptions.dataType = options.dataType || this.config.dataTypeoptions.url = options.baseUrl + options.urloptions.data = options.data || {}options.method = options.method || this.config.methodoptions.timeout = options.timeout || this.config.timeout; // 使用配置的超时时间return new Promise((resolve, reject) => {let _config = nulllet timeoutHandle = null;// 超时处理if (options.timeout) {timeoutHandle = setTimeout(() => {reject({errMsg: "request:fail timeout",timeout: true,requestId: requestId // 超时错误也携带ID,方便定位});}, options.timeout);}options.complete = (response) => {// 无论成功失败,都清除超时计时器clearTimeout(timeoutHandle);_config = Object.assign({}, this.config, options);response.config = _config; // 给响应挂载配置,供拦截器使用// 执行响应拦截器if (this.interceptor.response) {const interceptedResponse = this.interceptor.response(response);if (interceptedResponse !== undefined) {response = interceptedResponse;}}// 统一的响应日志记录_reslog(response)if (response.statusCode === 200) { //成功resolve(response);} else {reject(response)}}// 失败回调(网络错误等)options.fail = (error) => {clearTimeout(timeoutHandle);error.requestId = requestId; // 网络错误携带IDconsole.error(`【网络请求失败】ID: ${requestId}`, error);uni.showToast({title: error.timeout ? "请求超时" : "网络连接失败,请检查网络",icon: "none"});reject(error);};// 执行请求拦截器_config = Object.assign({}, this.config, options);if (this.interceptor.request) {const interceptedConfig = this.interceptor.request(_config);if (interceptedConfig !== undefined) {_config = interceptedConfig;}}// 统一的请求日志记录_reqlog(_config)uni.request(_config);});},get(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'GET'return this.request(options)},post(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'POST'return this.request(options)},put(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'PUT'return this.request(options)},delete(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'DELETE'return this.request(options)}
}/*** 请求接口日志记录*/function _reqlog(req) {//开发环境日志打印if (process.env.NODE_ENV === 'development') {const reqId = req.requestId || '未知ID'; // 读取请求IDconst reqUrl = req.url || '未知URL'; // 读取完整URLconsole.log(`【${reqId}】 地址:${reqUrl}`);if (req.data) {console.log("【" + (req.requestId || '未知ID') + "】 请求参数:", req.data);}}
}/*** 响应接口日志记录*/
function _reslog(res) {if (!res) {console.error("【日志错误】响应对象为空");return;}const requestId = res.config?.requestId || '未知请求ID';const url = res.config?.url || '未知URL';const statusCode = res.statusCode || '未知状态码';console.log(`【${requestId}】 接口: ${url} | 业务状态码: ${statusCode}`);// 打印响应数据if (res.data) {console.log(`【${requestId}】 响应数据:`, res.data);}// 错误处理逻辑switch (statusCode) {case "401":console.error(`【${requestId}】 未授权错误`);break;case "404":console.error(`【${requestId}】 接口不存在`);break;case "500":console.error(`【${requestId}】 服务器错误`);break;default:}
}
/*** 生成唯一请求ID(时间戳+随机数,避免重复)*/
function generateRequestId() {const timestamp = Date.now().toString(36); // 时间戳转36进制(缩短长度)const randomStr = Math.random().toString(36).slice(2, 8); // 6位随机字符串return `${timestamp}-${randomStr}`; // 格式:时间戳-随机串(如:1h8z2x-9k7a3b)
}

(2) 请求增强层 (http.js)

在基础层之上添加:

  • 全局拦截器(请求/响应)
  • 公共参数处理
  • 错误统一处理
  • 默认配置初始化
import uniRequest from './interface'uniRequest.config.baseUrl = "https://cn.bing.com"
uniRequest.config.header = {'Content-Type': 'application/x-www-form-urlencoded'
}// 公共参数(所有请求都会携带)
const commonParams = {};//设置请求前拦截器
uniRequest.interceptor.request = (config) => {//添加通用参数let token = uni.getStorageSync('token');if (token) {config.header["X-Token"] = token;}// 合并公共参数和业务参数const mergedData = {...commonParams, // 公共参数...config.data // 业务参数(会覆盖公共参数)};config.data = mergedData;return config;
}
//设置请求结束后拦截器
uniRequest.interceptor.response = (response) => {return response;
}export default uniRequest

(3) API接口层 (api/index.js)

import http from '@/common/vmeitime-http/http.js'const $api = {//查看必应图片byImageList(data) {return http.request({url: '/HPImageArchive.aspx?format=js&idx=0&n=1',method: 'GET',data,})}
}
export default $api

三、使用示例:调用Bing图片API

<template><view><!-- 显示加载状态 --><view v-if="loading" class="loading">{{ tooltips.loading }}</view><!-- 显示Bing图片 --><image v-else :src="fullImageUrl" mode="widthFix" class="bing-image"></image></view>
</template><script>export default {data() {return {loading: true, // 加载状态fullImageUrl: "", // 拼接后的完整图片URL};},onLoad() {uni.showLoading({title: '请稍后',mask: true // 防止用户重复操作});this.fetchBingImage().finally(() => {this.loading = false; // 确保 loading 被关闭uni.hideLoading(); // 无论成功失败都关闭加载框});},methods: {fetchBingImage() {return this.$api.byImageList().then(res => {console.log("请求成功:", res);console.log('images 数组:', res.data.images);// 2. 提取 images 数组的第一项const imageData = res.data.images[0];console.log('images图片:', imageData.url);// 3. 拼接完整URL(http://cn.bing.com + url)this.fullImageUrl = `https://cn.bing.com${imageData.url}`;console.log('images图片地址:', this.fullImageUrl);}).catch(err => {// 此时的err只可能是:HTTP错误、解析异常、业务失败(result≠0000)console.error("请求失败:", err);uni.showToast({title: err.error || err.timeout ? '请求超时' : '请求失败',icon: 'none',duration: 2000});return Promise.reject(err); // 继续抛出,供上层处理});},},};
</script><style>.loading {font-size: 16px;color: #999;text-align: center;margin-top: 50px;}.bing-image {width: 100%;height: auto;}
</style>

四、高级功能实现

1. 日志系统

// 请求日志
function _reqlog(req) {if (process.env.NODE_ENV === 'development') {console.log(`【${req.requestId}】 地址:${req.url}`);if (req.data) {console.log("请求参数:", req.data);}}
}// 响应日志
function _reslog(res) {const requestId = res.config?.requestId || '未知请求ID';console.log(`【${requestId}】 接口: ${res.config?.url} | 状态码: ${res.statusCode}`);// 错误状态码处理switch (res.statusCode) {case 401: console.error(`未授权错误`); break;case 404: console.error(`接口不存在`); break;case 500: console.error(`服务器错误`); break;}
}

2. 请求ID生成算法

function generateRequestId() {const timestamp = Date.now().toString(36); // 时间戳转36进制const randomStr = Math.random().toString(36).slice(2, 8); // 6位随机字符串return `${timestamp}-${randomStr}`; // 格式:时间戳-随机串
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/922462.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/922462.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

架构师成长之路-架构方法论

文章目录前言一、先搞懂&#xff1a;架构师不仅仅是“技术大佬”&#xff0c;更是“问题解决者”1.1 架构师的分类&#xff1a;不止“开发架构师”一种1.2 架构师要关注什么&#xff1f;别只盯着技术1.3 架构师解决问题的4步心法&#xff1a;从定义到落地1.4 架构师的成长攻略&…

uniapp在微信小程序中实现 SSE 流式响应

前言 最近需要使用uniapp开发一个智能对话页面&#xff0c;其中就需要使用SSE进行通信。 本文介绍下在uniapp中如何基于uni.request实现SSE流式处理。 在线体验 #小程序:yinuosnowball SSE传输格式 返回输出的流式块: Content-Type为text/event-stream 每个流式块均为 d…

STM32N6AI资料汇总

文章目录前言一、STM32N6硬件资源1.1 NUCLEO-N657X0-Q1.2 STM32N6570-DK1.3 正点原子STM32N647二、STM32N6软件资源2.1 STM32CubeN6例程资源包2.2 STM32图像信号处理器&#xff08;ISP&#xff09;调优软件2.3 正点原子N6开发板配套软件三、AI软件资源3.1 STM32N6 AI软件包总结…

Flask学习笔记(一)

1、环境准备pip install Flask使用Flask开发第1个入门程序&#xff1a;from flask import Flask app Flask(__name__) app.route(/) def hello_world():return Hello, World!if __name__ __main__:app.run()Flask构造函数将当前模块的名称(__name__)作为参数。2、route函数ap…

CSP认证练习题目推荐(4)

思维、贪心、综合 排队打水 这道题目不算难&#xff0c;但是不注意还是会出现很多错误&#xff0c;比如结构体的书写。以及自定义结构体排序。还有这里做的优化&#xff0c;使用前缀和记录打水的等待时间&#xff0c;但是这里很容易出错的点在于等待时间是应该是记录的前一个…

MySQL 视图的更新与删除:从操作规范到风险防控

MySQL 视图的更新与删除&#xff1a;从操作规范到风险防控 视图作为 “虚拟表”&#xff0c;其更新与删除操作常常让开发者困惑 ——“为什么更新视图会报错&#xff1f;”“删除视图会不会弄丢数据&#xff1f;” 实际上&#xff0c;80% 的视图操作问题都源于对 “视图依赖基表…

C 语言实现 I.MX6ULL 点灯(续上一篇)、SDK、deep及bsp工程管理

目录 一、汇编点灯转 C 语言实现 1. 关键字&#xff1a;volatile 2. 寄存器地址定义&#xff08;两种方式&#xff09; &#xff08;1&#xff09;直接宏定义地址 &#xff08;2&#xff09;结构体封装寄存器&#xff08;优化访问&#xff09; 3. 核心功能代码 &#xff…

DevOps实战(7) - 使用Arbess+GitPuk+sourcefare实现Node.js项目自动化部署

Arbess 是一款国产开源免费的 CI/CD 工具&#xff0c;工具支持一键部署&#xff0c;页面简洁易用。本文将详细介绍如何安装配置使用GitPuk、sourcefare、Arbess系统&#xff0c;使用流水线拉取GitPuk源码、使用sourcefare代码扫描、构建安装包并进行主机部署。 1、GitPuk 安装…

算法,蒜鸟蒜鸟-P1-理解“双指针”

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 有很多很多不足的地方&#xff0c;欢迎评论交流&#xff0c;感谢您的阅读和评论&#x1f604;。 目录引言1 双指针&#xff1a;Two Pointers1.1 左右指…

使用cookiecutter创建python项目

一、关于Python项目结构Python 项目并没有完全统一的 “固定结构”&#xff0c;但行业内有一些广泛遵循的约定俗成的目录结构&#xff08;尤其针对可分发的包或大型项目&#xff09;。同时&#xff0c;确实有工具可以快速生成这些标准化结构&#xff0c;提高开发效率&#xff0…

台积电生态工程深度解析:从晶圆厂到蜂巢的系统架构迁移

当半导体巨头将工厂视为生态系统&#xff0c;用工程思维解决环境问题概述&#xff1a;生态系统的工程化再造台积电近日开展的"积蜜"项目绝非简单的企业CSR行为&#xff0c;而是一场将生态系统视为复杂系统进行工程化改造的技术实践。本文将从系统架构、数据监控、循环…

从零实现一个简易计算器

最近在刷算法题时&#xff0c;遇到了实现计算器的问题。一开始觉得很简单&#xff0c;但真正动手实现时才发现其中有很多细节需要考虑。今天就来分享一下我的实现思路和学到的经验。问题分析我们需要实现一个能够处理加减乘除四则运算的计算器&#xff0c;要正确处理运算符的优…

Actix-webRust Web框架入门教程

文章目录引言Actix-web是什么&#xff1f;准备工作你的第一个Actix-web应用理解代码结构处理请求和响应接收请求数据返回响应中间件 - 增强你的应用状态管理和依赖注入实用示例&#xff1a;构建RESTful API测试你的Actix-web应用部署Actix-web应用结语额外资源引言 嘿&#xf…

若依框架前端通过 nginx docker 镜像本地运行

1. 前言 项目运行过程图&#xff1a;对于前端项目通过命令 npm run build 打包后&#xff0c;无法直接运行。存在如下错误&#xff1a;可以通过配置 nginx 服务器运行前端项目解决如上问题。 2. Nginx 运行 采用 docker 镜像的方式运行&#xff0c;docker-compose.yml 文件内容…

浅聊一下HTTP协议

在日常上网浏览网页、刷视频时&#xff0c;背后都离不开 HTTP 协议的支持。作为 Web 世界的 “交通规则”&#xff0c;它负责服务器和客户端浏览器之间的数据传输。这篇文章就带大家全面了解 HTTP 协议&#xff0c;从基本概念到通信细节&#xff0c;再到安全相关的 HTTPS&#…

机器人控制器开发(定位——cartographer ros2 使用2)

文章总览 1 纯定位模式 当完成建图后&#xff0c;会生成pbstream格式的地图文件 配置纯定位模式的lua脚本 backpack_2d_localization.lua include "backpack_2d.lua"TRAJECTORY_BUILDER.pure_localization_trimmer {max_submaps_to_keep 3, } POSE_GRAPH.optimi…

《大数据之路1》笔记3:数据管理

一 元数据 1.1 元数据概述 定义&#xff1a; 元数据是关于数据的数据&#xff0c;元数据打通了源数据、数据仓库、数据应用&#xff0c;记录了数据从生产到消费的全部过程。元数据主要记录数据仓库中模型的定义、各层级间的映射关系、监控数据仓库的数据状态和ETL的任务运行状态…

排序实现java

排序算法概述Java中实现排序可以通过多种方式&#xff0c;包括内置方法、自定义算法或使用第三方库。常见的排序算法有冒泡排序、选择排序、插入排序、快速排序、归并排序等。使用Arrays.sort()方法对于数组排序&#xff0c;Java提供了Arrays.sort()方法&#xff0c;支持对基本…

51c大模型~合集182

我自己的原文哦~ https://blog.51cto.com/whaosoft/14174587 #LaV-CoT 超越GPT-4o&#xff0c;蚂蚁集团与南洋理工大学提出&#xff1a;首个语言感知的视觉思维链 随着大型视觉语言模型&#xff08;VLM&#xff09;的飞速发展&#xff0c;它们在处理复杂的视…

C++ STL之deque的使用和模拟实现

目录 deque 核心本质与定位 与stack和queue的关系: deque的使用 deque的底层实现 deque的原理介绍 deque的缺陷 总结: deque deque文档 : deque 翻译: 双端队列 deque&#xff08;通常发音类似“deck”&#xff09;是“double-ended queue”&#xff08;双端队列&…