uni-app三部曲之二: 封裝http請求

1.引言

前面一篇文章寫了使用Pinia進行全局狀態管理。

這篇文章主要介紹一下封裝http請求,發送數據請求到服務端進行數據的獲取。

感謝:

1.yudao-mall-uniapp: 芋道商城,基于 Vue + Uniapp 實現,支持分銷、拼團、砍價、秒殺、優惠券、積分、會員等級、小程序直播、頁面 DIY 等功能,100% 開源

2.3.x文檔 | luch-request

3.Day1-01-uni-app小兔鮮兒導學視頻_嗶哩嗶哩_bilibili

2.token過期后的重新獲取思路

在進行登錄后,通過本地緩存,存儲獲取到的accessToken與refreshToken,accessToken的過期時間為30分鐘,refreshToken過期時間為30天。在每次發送請求時,通過http的請求攔截器,放入accessToken進入header中,后端進行校驗,當accessToken過期后,后端返回的封裝中,code為401,此時應該用refreshToken無感知刷新accessToken繼續本次的請求,當refreshToken也過期后,就需要用戶重新進行登錄。

3.代碼

代碼主要介紹三個部分,第一部分是自定義http的請求攔截器與響應攔截器,第二部分是封裝http的請求,第三部分是如何發送具體的請求。

1.自定義攔截器

請求攔截器主要定義發送請求時的參數,響應攔截器主要處理返回時各種情況。具體可查看文檔

import { getRefreshToken, getAccessToken, setAccessToken } from '@/utils/auth'
import { platform } from '@/utils/platform'
import { useUserStore } from '@/store'
import Request from 'luch-request'
import * as authApi from '@/api/auth'const options = {// 顯示操作成功消息 默認不顯示showSuccess: false,// 成功提醒 默認使用后端返回值successMsg: '',// 顯示失敗消息 默認顯示showError: true,// 失敗提醒 默認使用后端返回信息errorMsg: '',// 顯示請求時loading模態框 默認顯示showLoading: true,// loading提醒文字loadingMsg: '加載中',// 需要授權才能請求 默認放開auth: false,// ...
}// Loading全局實例
const LoadingInstance = {target: null,count: 0,
}/*** 關閉loading*/
function closeLoading() {if (LoadingInstance.count > 0) LoadingInstance.count--if (LoadingInstance.count === 0) uni.hideLoading()
}
/*** @description 請求基礎配置 可直接使用訪問自定義請求*/
const http = new Request({// 請求基準地址baseURL: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL,timeout: 8000,header: {Accept: '*/*','Content-Type': 'application/json;charset=UTF-8',platform,},// #ifdef APP-PLUSsslVerify: false,// #endif// #ifdef H5// 跨域請求時是否攜帶憑證(cookies)僅H5支持(HBuilderX 2.6.15+)withCredentials: false,// #endifcustom: options,
})/*** @description 請求攔截器*/
http.interceptors.request.use((config) => {// 自定義處理【loading 加載中】:如果需要顯示 loading,則顯示 loadingif (config.custom.showLoading) {LoadingInstance.count++LoadingInstance.count === 1 &&uni.showLoading({title: config.custom.loadingMsg,mask: true,fail: () => {uni.hideLoading()},})}// 添加 token 請求頭標識const token = getAccessToken()if (token) {config.header.Authorization = `Bearer ${token}`}return config},(error) => {return Promise.reject(error)},
)/*** @description 響應攔截器*/
http.interceptors.response.use((response) => {// 自定處理【loading 加載中】:如果需要顯示 loading,則關閉 loadingresponse.config.custom.showLoading && closeLoading()// 返回結果:包括 code + data + msgconst resData = response.dataconst code = resData.codeif (code === 200) {return Promise.resolve(response.data)} else if (code === 401) {return refreshToken(response.config)} else {uni.showToast({title: resData.message || '出錯啦!',icon: 'none',mask: true,})}},(error) => {let errorMessage = '網絡請求出錯'if (error !== undefined) {switch (error.statusCode) {case 400:errorMessage = '請求錯誤'breakcase 401:errorMessage = '請登錄'// 正常情況下,后端不會返回 401 錯誤,所以這里不處理 handleAuthorizedbreakcase 403:errorMessage = '拒絕訪問'breakcase 404:errorMessage = '請求出錯'breakcase 408:errorMessage = '請求超時'breakcase 429:errorMessage = '請求頻繁, 請稍后再訪問'breakcase 500:errorMessage = '服務器開小差啦,請稍后再試~'breakcase 501:errorMessage = '服務未實現'breakcase 502:errorMessage = '網絡錯誤'breakcase 503:errorMessage = '服務不可用'breakcase 504:errorMessage = '網絡超時'breakcase 505:errorMessage = 'HTTP 版本不受支持'break}if (error.errMsg.includes('timeout')) errorMessage = '請求超時'// #ifdef H5if (error.errMsg.includes('Network'))errorMessage = window.navigator.onLine ? '服務器異常' : '請檢查您的網絡連接'// #endif}if (error && error.config) {if (error.config.custom.showError === false) {uni.showToast({title: error.data?.msg || errorMessage,icon: 'none',mask: true,})}error.config.custom.showLoading && closeLoading()}return false},
)// Axios 無感知刷新令牌,參考 https://www.dashingdog.cn/article/11 與 https://segmentfault.com/a/1190000020210980 實現
let requestList = [] // 請求隊列
let isRefreshToken = false // 是否正在刷新中
const refreshToken = async (config) => {// 如果當前已經是 refresh-token 的 URL 地址,并且還是 401 錯誤,說明是刷新令牌失敗了,直接返回 Promise.reject(error)if (config.url.indexOf('/auth/refresh-token') >= 0) {isRefreshToken = falseuni.navigateTo({ url: '/pages/login/index' })return Promise.reject(new Error('error'))}console.log('過期', config)// 如果未認證,并且未進行刷新令牌,說明可能是訪問令牌過期了if (!isRefreshToken) {isRefreshToken = true// 1. 如果獲取不到刷新令牌,則只能執行登出操作const refreshToken = getRefreshToken()if (!refreshToken) {return handleAuthorized()}// 2. 進行刷新訪問令牌const refreshTokenData = reactive({refreshToken: getRefreshToken(),clientId: import.meta.env.VITE_CLIENT_ID,})const res = await authApi.refreshToken(refreshTokenData)console.log(res)setAccessToken(res.data.accessToken)try {// 2.1 刷新成功,則回放隊列的請求 + 當前請求config.header.Authorization = 'Bearer ' + getAccessToken()requestList.forEach((cb) => {cb()})requestList = []return request(options)} catch (e) {// 為什么需要 catch 異常呢?刷新失敗時,請求因為 Promise.reject 觸發異常。// 2.2 刷新失敗,只回放隊列的請求requestList.forEach((cb) => {cb()})// 提示是否要登出。即不回放當前請求!不然會形成遞歸return handleAuthorized()} finally {requestList = []isRefreshToken = false}} else {// 添加到隊列,等待刷新獲取到新的令牌return new Promise((resolve) => {console.log('重試', config)requestList.push(() => {config.header.Authorization = 'Bearer ' + getAccessToken() // 讓每個請求攜帶自定義token 請根據實際情況自行修改resolve(request(options))})})}
}/*** 處理 401 未登錄的錯誤*/
const handleAuthorized = () => {const userStore = useUserStore()userStore.userLogout()isRefreshToken = false// 是否進入登錄頁uni.showModal({title: '提示',content: '重新登錄?',success: function (res) {if (res.confirm) {uni.navigateTo({ url: '/pages/login/index' })}},})// 登錄超時return new Promise<IResData<boolean>>((resolve, reject) => {const res: IResData<boolean> = {code: 401,message: '請重新登錄',data: false,}reject(res)})
}const request = (config) => {return http.middleware(config)
}export default request
auth.ts/*** 存儲用戶身份信息令牌*/export const CACHE_KEY = {ACCESS_TOKEN: 'access_token',REFRESH_TOKEN: 'refresh_token',
}// 存儲訪問令牌
export const setAccessToken = (accessToken: string) => {uni.setStorageSync(CACHE_KEY.ACCESS_TOKEN, accessToken)
}// 存儲刷新令牌
export const setRefreshToken = (refreshToken: string) => {uni.setStorageSync(CACHE_KEY.REFRESH_TOKEN, refreshToken)
}// 獲取訪問令牌
export const getAccessToken = () => {return uni.getStorageSync(CACHE_KEY.ACCESS_TOKEN)
}// 獲取刷新令牌
export const getRefreshToken = () => {return uni.getStorageSync(CACHE_KEY.REFRESH_TOKEN)
}// 清理本地所有緩存
export const clearStorage = () => {uni.clearStorageSync()
}

2.封裝http請求

/*** 封裝不同類型的restful請求*/import request from './request'// 全局要用的類型放到這里type IResData<T> = {code: numbermessage: stringdata: T
}export default {get: async <T = any>(options: any) => {const res = await request({ method: 'GET', ...options })return res as unknown as IResData<T>},post: async <T = any>(option: any) => {const res = await request({ method: 'POST', ...option })return res as unknown as IResData<T>},postOriginal: async (option: any) => {const res = await request({ method: 'POST', ...option })return res},delete: async <T = any>(option: any) => {const res = await request({ method: 'DELETE', ...option })return res as unknown as IResData<T>},put: async <T = any>(option: any) => {const res = await request({ method: 'PUT', ...option })return res as unknown as IResData<T>},download: async <T = any>(option: any) => {const res = await request({ method: 'GET', responseType: 'blob', ...option })return res as unknown as Promise<T>},upload: async <T = any>(option: any) => {option.headersType = 'multipart/form-data'const res = await request({ method: 'POST', ...option })return res as unknown as Promise<T>},
}

3.定義請求

import http from '@/service/http'/** 用戶登錄 */
export const login = (data: LoginReqVO) => {return http.post({ url: '/auth/login', data })
}

4.寫在最后

在本項目開始,使用了uni.request來發送http請求,通過uni-app的攔截器配置請求攔截器,后面學習研究的時候發現了luch-request,通過文檔然后參考yudao-mall-uniapp項目,封裝http請求,通過測試,發現能滿足實際需用需求。

當然,本篇文章寫的比較簡陋,水平有限,歡迎共同探討指教。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44178.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44178.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44178.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電腦自動重啟是什么原因呢?99%人都不知道的解決辦法,直接打破循環

當你的電腦突然毫無預警地自動重啟&#xff0c;不僅打斷了工作流程&#xff0c;還可能導致未保存的數據丟失&#xff0c;這無疑是一件令人沮喪的事情。那么&#xff0c;電腦自動重啟是什么原因呢&#xff1f;有什么方法可以解決呢&#xff1f;別擔心&#xff0c;在大多數情況下…

Android Retrofit post請求,@Body傳遞的參數轉義問題

文章目錄 問題解決原因解決方案一&#xff1a;自己拼接json字符串&#xff0c;Body使用RequestBody類型&#xff0c;比如解決方案二&#xff1a;修改Retrofit的Gson 問題 因為傳遞的參數字符串中有等號 &#xff0c;結果傳遞的時候&#xff0c;打印出來 原始字符串&#xff…

【AIGC】GPT-4深度解析:自然語言處理的新紀元

目錄 第一部分&#xff1a;GPT-4技術概覽 1.1 GPT-4模型架構 多模態輸入處理 專家混合&#xff08;MoE&#xff09;技術詳解 參數規模和模型復雜性 1.2 GPT-4的關鍵技術創新 上下文窗口的擴展 模型性能預測技術 1.3 GPT-4與其他模型的比較 性能對比 架構差異 第二部…

docker-2

27.構建python應用鏡像-dockerfile實踐項目 1.基于官方的鏡像&#xff0c;構建python代碼運行環境 dockerfile 2.運行鏡像&#xff0c;開啟一個讀寫的容器空間&#xff08;定制操作&#xff0c;將代碼丟進去&#xff0c;運行調試&#xff09; 3.提交這個變化的容器層數據&#…

cal命令

1、命令詳解&#xff1a; cal&#xff08;全稱&#xff1a;Calendar&#xff09;該命令用來顯示當前日歷或者指定日期的公歷。 2、官方參數&#xff1a; -1, --one 僅顯示當前月份&#xff08;默認&#xff09;-3, --three 顯示上個月、當前月和下個月-s, --sunday…

谷粒商城P85發布商品時規格參數不顯示問題

P85講&#xff0c;發布商品&#xff0c;點擊下一步之后&#xff0c;發現規格參數不顯示 打開控制臺發現報錯forEach...錯誤 查了問題原因&#xff0c;發現返回的分組中個別組的關聯屬性(attrs)可能為null 所以這個時候&#xff0c;需要確保后端返回的attrs不能為null 方式1…

數據結構之順序存儲線性表實現詳解與示例(C,C#,C++)

文章目錄 一、順序存儲線性表的基本概念二、順序存儲線性表的實現1、數據結構定義2、初始化3、添加元素4、訪問元素5、修改元素6、刪除元素7、銷毀 三、示例C語言示例C#語言示例C語言示例 順序存儲線性表是一種基本的數據結構&#xff0c;它將線性表的元素按照一定的順序存放在…

Mysql中存儲過程、存儲函數、自定義函數、變量、流程控制語句、光標/游標、定義條件和處理程序的使用示例

場景 存儲過程 存儲過程是一組為了完成特定功能的SQL語句集合。使用存儲過程的目的是將常用或復雜的工作預先用SQL語句寫好并用一個指定名稱存儲起來&#xff0c; 這個過程經編譯和優化后存儲在數據庫服務器中&#xff0c;因此稱為存儲過程。 當以后需要數據庫提供與己定義…

分享WPF的UI開源庫

文章目錄 前言一、HandyControl二、AduSkin三、Adonis UI四、Panuon.WPF.UI五、LayUI-WPF六、MahApps.Metro七、MaterialDesignInXamlToolkit八、FluentWPF九、DMSkin總結 前言 分享WPF的UI開源庫。 一、HandyControl HandyControl是一套WPF控件庫&#xff0c;它幾乎重寫了所…

uni-app 掃描二維碼獲取信息功能

首先是掃描二維碼的功能&#xff0c;可以參考這篇博文 uni-app-H5頁面調用設備攝像頭掃描二維碼_uni-app app端調用攝像頭顯示至指定元素上顯示-CSDN博客 然后現在是可以掃描二維碼的狀態&#xff0c;掃描之后&#xff0c;可以看到首先是出發上一個頁面的事件&#xff0c;然后…

每天一個數據分析題(四百二十五)- 單因素方差分析

關于下表&#xff0c;錯誤說法是&#xff08; &#xff09; A. 這是單因素方差分析的輸出結果 B. 表中 F< F crit, 與 P-value 大于顯著性水平是等價的 C. 表內組間均方差沒有顯著大于組內均方差 D. 由于組內SS數值顯著大于組間SS&#xff0c;因此可以推斷不同分類對于…

使用Python繪制面積圖

使用Python繪制面積圖 面積圖效果代碼 面積圖 面積圖展示數據隨時間的累積變化&#xff0c;適合表現趨勢和總量。通過填充圖形下方的區域&#xff0c;可以直觀地顯示各時間點的數值及其變化。 效果 [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-…

機器學習——決策樹(筆記)

目錄 一、認識決策樹 1. 介紹 2. 決策樹生成過程 二、sklearn中的決策樹 1. tree.DecisionTreeClassifier&#xff08;分類樹&#xff09; &#xff08;1&#xff09;模型基本參數 &#xff08;2&#xff09;模型屬性 &#xff08;3&#xff09;接口 2. tree.Decision…

最新開源免費數字人工具

使用步驟更是簡單到不行&#xff1a; 1. 輸入圖片&#xff1a;選擇你想要生成動態視頻的肖像圖片。 2. 輸入音頻&#xff1a;提供與圖片匹配的音頻文件&#xff0c;EchoMimic會根據音頻內容驅動肖像的動態效果。 3. 設置參數&#xff1a;一般保持默認設置即可&#xff0c;當然&…

排序題目:最小時間差

文章目錄 題目標題和出處難度題目描述要求示例數據范圍 解法思路和算法代碼復雜度分析 題目 標題和出處 標題&#xff1a;最小時間差 出處&#xff1a;539. 最小時間差 難度 3 級 題目描述 要求 給定一個 24 \texttt{24} 24 小時制的時間列表&#xff0c;時間以 &quo…

暗黑魅力:Xcode全面擁抱應用暗黑模式開發指南

暗黑魅力&#xff1a;Xcode全面擁抱應用暗黑模式開發指南 隨著蘋果在iOS 13和iPadOS 13中引入暗黑模式&#xff0c;用戶可以根據自己的喜好或環境光線選擇不同的界面主題。作為開發者&#xff0c;支持暗黑模式不僅能提升用戶體驗&#xff0c;還能彰顯應用的專業性。Xcode提供了…

《夢醒蝶飛:釋放Excel函數與公式的力量》11.4 ISERROR函數

第11章&#xff1a;信息函數 第四節 11.4 ISERROR函數 11.4.1 簡介 ISERROR函數是Excel中的一個信息函數&#xff0c;用于檢查指定單元格或表達式是否產生錯誤。如果單元格或表達式產生任何類型的錯誤&#xff08;如N/A、VALUE!、REF!等&#xff09;&#xff0c;則返回TRUE&…

全開源TikTok跨境商城源碼/TikTok內嵌商城+搭建教程/前端uniapp+后端

多語言跨境電商外貿商城 TikTok內嵌商城&#xff0c;商家入駐一鍵鋪貨一鍵提貨 全開源完美運營 海外版抖音TikTok商城系統源碼&#xff0c;TikToK內嵌商城&#xff0c;跨境商城系統源碼 接在tiktok里面的商城。tiktok內嵌&#xff0c;也可單獨分開出來當獨立站運營 二十一種…

FPGA原型驗證(八):如何選擇現成的原型驗證平臺?

第6章 如何選擇現成的原型驗證平臺? 在第5章中,我們探討了為基于FPGA的原型項目創建FPGA硬件平臺時應考慮的詳細因素。 現在,我們將考慮所謂的“自制還是購買”爭論的另一方面。什么時候使用現成的FPGA板或甚至是更復雜的基于FPGA的系統,而不是設計定制板更有意義? 什么…

leetcode165.解密數字

題目表述&#xff1a; 這道題目和斐波那契數列以及跳臺階問題十分相似。 斐波那契數列&#xff1a;0、1、1、2、3、5, 8、13、21、34 …… leetcode跳臺階問題&#xff1a;1、1、2、3、5, 8、13、21、34....... 這類題目的特點都是第N項的結果等于前兩項的和。 但是解密數…