尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

六、uni-app拦截器与请求封装

六、uni-app拦截器与请求封装 一、拦截器拦截器添加与删除添加拦截器uni.addInterceptor(STRING, OBJECT)STRING需要拦截的 API 名称如request、navigateTo。OBJECT拦截器对象包含invoke、success、fail、complete等生命周期钩子。删除拦截器uni.removeInterceptor(STRING, OBJECT?)STRING需要删除拦截器的 API 名称。OBJECT可选要移除的具体拦截器对象。如果不传此参数则会移除该 API 下的所有拦截器。示例 1基础添加与全局移除// 1. 添加拦截器uni.addInterceptor(request,{invoke(args){console.log(请求即将发送URL为,args.url);// 可以在这里修改参数例如拼接域名},success(res){console.log(请求成功返回数据,res);}});// 2. 移除拦截器不传第二个参数移除 request 上的所有拦截器uni.removeInterceptor(request);示例 2精准移除指定的拦截器⚠️ 易错点// 定义一个拦截器函数必须保持引用一致constmyInvokeFn(param){console.log(拦截触发,param);};// 1. 添加拦截器uni.addInterceptor(request,{invoke:myInvokeFn});// 2. 错误用法无法移除因为对象引用不同uni.removeInterceptor(request,{invoke:(param){console.log(拦截触发,param);}});// 3. 正确用法精准移除uni.removeInterceptor(request,{invoke:myInvokeFn});⚠️ 易错点uni.removeInterceptor第二个参数需要传入同一份函数引用如果重新写一个匿名函数引用会不同导致无法精准移除。示例 3路由拦截器的添加与移除exportdefault{onLoad(){// 页面加载时添加路由跳转拦截器uni.addInterceptor(navigateTo,{invoke(args){console.log(即将跳转到,args.url);// 如果返回 false则阻止跳转returntrue;}});},onUnload(){// 页面卸载时移除该拦截器uni.removeInterceptor(navigateTo);}};示例 4uniCloud 云端请求的拦截与移除// 添加云端拦截器uniCloud.addInterceptor(callFunction,{invoke(param){console.log(调用云函数,param.name);}});// 移除云端拦截器移除 callFunction 的所有拦截器uniCloud.removeInterceptor(callFunction);2. 拦截器生命周期钩子钩子名称触发时机核心作用与实战场景返回值说明invoke(args)API 调用前触发最常用。用于修改请求参数如拼接域名、注入 Token、权限校验或日志记录。返回false可阻止 API 执行并触发fail修改args可直接改变传入参数。success(res)API 成功回调时触发统一处理成功响应。例如统一提取res.data字段、处理特定的业务状态码如 401 跳登录页。修改res可改变传给业务层的数据。fail(err)API 失败回调时触发统一捕获异常。例如弹出网络错误提示、上报错误日志。无特殊返回值要求。complete(res)API 调用结束后触发无论成功或失败都会执行。常用于清理工作如隐藏全局 Loading 动画。无特殊返回值要求。returnValue(args)方法调用后触发用于处理最终的返回值。例如直接返回args.data让业务层调用时省略.data。返回值会替换原始的 API 返回结果。3. 拦截场景拦截分类拦截目标 (STRING)典型应用场景核心注意事项网络请求类request1. 统一拼接 BaseURL2. 自动注入 Token / 请求头3. 统一处理 401 等状态码uniCloud的云端请求底层也会使用此接口需注意过滤避免冲突。文件上传类uploadFile上传头像、凭证等文件时自动注入 Token 和拼接域名。返回的data是字符串需手动JSON.parse解析。文件下载类downloadFile下载合同、附件等文件时自动注入 Token 和拼接域名。官方文档未明确支持此拦截部分版本可能不生效建议在download方法内手动处理。普通页面跳转navigateTo1. 列表页跳转到详情页2. 常规的业务流程流转1. 不能跳转到tabBar页面。2. 页面栈最多叠加 10 层超出会静默失败。替换式跳转redirectTo1. 登录成功后跳转首页防回退2. 表单提交完成跳转结果页1. 不能跳转到tabBar页面。2. 适合“一次性”操作完成后的跳转防止重复触发。全清重置跳转reLaunch1. 退出登录 / 切换账号2. 应用重置 / 支付成功清空历史1. 可以跳转到任意页面包含tabBar页。2. 属于破坏性操作用户无法回退需谨慎使用。Tab 页切换switchTab1. 底部 Tab 栏切换2. 业务流程结束回到主页1. 只能跳转到pages.json中配置的tabBar页面。2. URL 后不能带参数传参需走全局状态管理。异步存储类setStoragegetStorage统一对存入缓存的数据进行加密/解密或统一格式化 JSON。仅支持异步接口不支持setStorageSync等同步方法。4. 避坑指南必须“四管齐下”在编写路由守卫时必须显式注册全部四个拦截器uni.addInterceptor(navigateTo, ...)等漏掉任意一个权限逻辑就可能被绕过。路径标准化处理拦截器拿到的args.url可能带有查询参数如/pages/user/profile?id123。在做白名单匹配时务必先截断问号后的内容args.url.split(?)[0]否则会导致匹配失败。注意原生组件盲区uni.addInterceptor只能拦截通过 JS 层主动发起的跳转。如果用户在页面中使用了navigator原生组件进行跳转拦截器是无法拦截的。防无限循环在拦截器内部执行跳转例如未登录时跳转登录页时一定要做好防递归处理避免陷入死循环。二、请求封装示例第一步核心请求封装 (utils/http.js)// utils/http.jsimport{useMemberStore}from/stores// 基础域名配置constbaseURLhttps://pcapi-xiaotuxian-front-devtest.itheima.net// 1. 定义公共拦截器对象consthttpInterceptor{invoke(options){// 1.1 非 http 开头需拼接地址if(!options.url.startsWith(http)){options.urlbaseURLoptions.url}// 1.2 设置请求超时时间 (10秒)options.timeout10000// 1.3 添加小程序端请求头标识options.header{...options.header,source-client:miniapp,}// 1.4 自动注入 TokenconstmemberStoreuseMemberStore()consttokenmemberStore.profile?.tokenif(token){options.header.Authorizationtoken}},}// 2. 全局安装拦截器 (同时拦截普通请求和文件上传)uni.addInterceptor(request,httpInterceptor)uni.addInterceptor(uploadFile,httpInterceptor)// ⚠️ 注意uni.downloadFile 不支持 addInterceptor我们在 download 方法里手动处理/** * 3. 封装普通请求 (GET/POST/PUT/DELETE) */exportconsthttp(options){returnnewPromise((resolve,reject){uni.request({...options,success(res){if(res.statusCode200res.statusCode300){resolve(res.data)}elseif(res.statusCode401){constmemberStoreuseMemberStore()memberStore.clearProfile()uni.navigateTo({url:/pages/login/login})reject(res)}else{uni.showToast({icon:none,title:res.data?.msg||请求错误})reject(res)}},fail(err){uni.showToast({icon:none,title:网络错误换个网络试试})reject(err)},})})}/** * 4. 封装文件上传 */exportconstupload(options){returnnewPromise((resolve,reject){uni.uploadFile({...options,success(res){// ⚠️ 坑点uploadFile 返回的 data 是字符串必须手动 JSON.parseconstdataJSON.parse(res.data)if(res.statusCode200res.statusCode300){resolve(data)}else{uni.showToast({icon:none,title:data.msg||上传失败})reject(data)}},fail(err){uni.showToast({icon:none,title:上传失败请重试})reject(err)},})})}/** * 5. 封装文件下载 */exportconstdownload(options){returnnewPromise((resolve,reject){// 手动拼接域名和 Token (因为 downloadFile 不支持拦截器)leturloptions.urlif(!url.startsWith(http))urlbaseURLurlconstmemberStoreuseMemberStore()consttokenmemberStore.profile?.tokenconstheader{...options.header}if(token)header.Authorizationtoken uni.downloadFile({url,header,success(res){if(res.statusCode200res.statusCode300){// 下载成功返回临时文件路径 (tempFilePath)resolve(res.tempFilePath)}else{uni.showToast({icon:none,title:下载失败})reject(res)}},fail(err){uni.showToast({icon:none,title:网络错误下载失败})reject(err)},})})}核心要点拦截器通过uni.addInterceptor同时拦截request和uploadFile统一完成域名拼接、超时设置、请求头标识和 Token 注入。401 处理普通请求遇到 401 时自动清理用户信息并跳转登录页。上传解析uploadFile返回的data是字符串必须手动JSON.parse。下载限制uni.downloadFile不支持addInterceptor需手动拼接域名和 Token。第二步API 接口集中管理 (api/file.js)普通数据请求 (api/goods.js)// api/goods.jsimport{http}from/utils/http// 1. 查 (GET) - 获取商品列表exportconstgetGoodsListAPI(params){returnhttp({method:GET,url:/goods/list,data:params,// GET 请求的参数会自动拼接到 URL 后面})}// 2. 增 (POST) - 新增商品exportconstpostGoodsAPI(data){returnhttp({method:POST,url:/goods,data,})}// 3. 改 (PUT) - 修改商品exportconstputGoodsAPI(id,data){returnhttp({method:PUT,url:/goods/${id},// 将 ID 拼接在 URL 路径中data,})}// 4. 删 (DELETE) - 删除商品exportconstdeleteGoodsAPI(id){returnhttp({method:DELETE,url:/goods/${id},})}文件操作请求 (api/file.js)// api/file.jsimport{upload,download}from/utils/http// 文件上传示例 (比如上传头像)exportconstuploadAvatarAPI(filePath){returnupload({url:/member/upload-avatar,// 后端接口地址filePath:filePath,// 本地临时文件路径name:file,// 后端接收文件的字段名})}// 文件下载示例 (比如下载合同PDF)exportconstdownloadContractAPI(contractId){returndownload({url:/contract/download/${contractId},})}第二步接口说明接口函数请求方式说明getGoodsListAPI(params)GET获取商品列表参数会自动拼接 URLpostGoodsAPI(data)POST新增商品putGoodsAPI(id, data)PUT修改指定商品ID 拼接在路径中deleteGoodsAPI(id)DELETE删除指定商品uploadAvatarAPI(filePath)上传上传头像返回服务器图片地址downloadContractAPI(contractId)下载下载合同 PDF返回临时文件路径第三步页面优雅调用 (pages/goods/goods.vue)template view classgoods-page text classtitle商品管理/text !-- 1. 查展示商品列表 -- view v-foritem in goodsList :keyitem.id classgoods-item text{{ item.name }} - ¥{{ item.price }}/text view classbtn-group button sizemini clickhandleUpdate(item)修改/button button sizemini typewarn clickhandleDelete(item.id)删除/button button sizemini typedefault clickhandleDownload(item.id)下载说明书/button /view /view !-- 2. 增新增商品按钮 -- button clickhandleAdd classaction-btn新增商品/button !-- 3. 上传为商品上传图片 -- button clickhandleUpload classaction-btn上传商品图片/button /view /template script setup import { ref, onMounted } from vue // 引入增删改查接口 import { getGoodsListAPI, postGoodsAPI, putGoodsAPI, deleteGoodsAPI } from /api/goods // 引入文件操作接口 import { uploadAvatarAPI, downloadContractAPI } from /api/file // 响应式列表数据 const goodsList ref([]) // 增删改查 (CRUD) 操作 // 查获取商品列表 const fetchGoodsList async () { try { const res await getGoodsListAPI({ page: 1, pageSize: 10 }) goodsList.value res.result || [] } catch (error) { console.error(获取商品列表失败:, error) } } // 增新增商品 const handleAdd async () { try { await postGoodsAPI({ name: 前端大师同款键盘, price: 299.00, }) uni.showToast({ title: 新增成功, icon: success }) fetchGoodsList() // 刷新列表 } catch (error) { console.error(新增商品失败:, error) } } // 改修改商品 const handleUpdate async (item) { try { await putGoodsAPI(item.id, { ...item, price: 199.00 // 假设降价促销 }) uni.showToast({ title: 修改成功, icon: success }) fetchGoodsList() // 刷新列表 } catch (error) { console.error(修改商品失败:, error) } } // 删删除商品 const handleDelete async (id) { try { await deleteGoodsAPI(id) uni.showToast({ title: 删除成功, icon: success }) fetchGoodsList() // 刷新列表 } catch (error) { console.error(删除商品失败:, error) } } // 文件操作 (Upload / Download) // 上传选择图片并上传 const handleUpload () { uni.chooseImage({ count: 1, success: async (res) { try { const tempFilePath res.tempFilePaths[0] // 调用文件上传接口 const uploadRes await uploadAvatarAPI(tempFilePath) uni.showToast({ title: 图片上传成功, icon: success }) console.log(服务器返回的图片地址:, uploadRes.result) } catch (error) { console.error(图片上传失败:, error) } }, }) } // 下载下载商品说明书 PDF const handleDownload async (goodsId) { try { // 调用文件下载接口获取临时文件路径 const tempPath await downloadContractAPI(goodsId) // 拿到路径后调用微信原生能力打开文档 uni.openDocument({ filePath: tempPath, showMenu: true, // 允许用户转发或保存到手机 success: () console.log(打开说明书成功), }) } catch (error) { console.error(说明书下载失败:, error) } } // 页面加载时获取数据 onMounted(() { fetchGoodsList() }) /script页面调用要点列表渲染使用v-for遍历商品列表key取商品id按钮按需绑定修改、删除、下载事件。操作后刷新增删改成功并弹出提示后统一调用fetchGoodsList()重新获取最新列表数据。上传流程通过uni.chooseImage获取本地临时文件路径再交给uploadAvatarAPI上传。下载流程下载成功后拿到tempFilePath再通过uni.openDocument打开或分享文档。页面初始化在onMounted中调用一次fetchGoodsList()保证进入页面时自动加载数据。
返回列表