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/diannao/43215.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/43215.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/43215.shtml

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

相關文章

實用性提升百分之一百!!!【ONLYOFFICE 8.1版本】全方位深度性能測評

目錄 【ONLYOFFICE 8.1 版本】全方位深度性能測評 一、界面與用戶體驗 二、文字處理功能 表格處理功能 演示文稿功能 協作與共享功能 性能與穩定性 總結 【ONLYOFFICE 8.1 版本】全方位深度性能測評 在當今數字化辦公的時代&#xff0c;辦公軟件的選擇對于提高工作效率和…

selenium處理cookie問題實戰

1. cookie獲取不完整 需要進入的資損平臺(web)首頁&#xff0c;才會出現有效的ctoken等信息 1.1. 原因說明 未進入指定頁面而獲取的 cookie 與進入頁面后獲取的 cookie 可能會有一些差異&#xff0c;這取決于網站的具體實現和 cookie 的設置方式。 通常情況下&#xff0c;一些…

x-mind沒有配置文件,可以自己創建文件修改內存

x-mind.ini 是 XMind 配置文件&#xff0c;用于自定義啟動選項。以下是一個完整的 x-mind.ini 文件示例。此配置假設你正在使用 Windows 系統并希望配置一些常見的啟動參數&#xff1a; -startup plugins/org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar --launcher.l…

query和params的區別是什么

query 和 params 是 Vue Router 中傳遞路由參數的兩種不同方式。它們的主要區別在于 URL 結構和獲取參數的方法。 1. params params 是用來傳遞路由路徑中的動態參數。這些參數在路由路徑中以冒號 (:) 表示。 路由配置&#xff1a; {path: /interviewApplication/:backpack…

NCCL源碼詳解6:通信拓撲識別感知構建 物理拓撲xml文件 ncclTopoGetSystem() 視頻教程

Nvidia NCCL如何構建物理拓撲 視頻教程在這&#xff1a; 2.2 NCCL源碼分析&#xff1a;物理拓撲識別感知xml通信topo構建 ncclTopoGetSystem()_嗶哩嗶哩_bilibili 一、ncclTopoGetSystem()拓撲構建 1.1 ncclTopoGetSystem()拓撲構建核心邏輯&#xff1a; 1、 嘗試從文件加…

【解決Windows11系統Windows Hello不能使用的問題】

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、Windows Hello是什么&#xff1f;二、使用步驟1.購買一個攝像頭2.開始配置 三、異常解決1.內置管理員不能使用2.沒找到合適的攝像頭3.攝像頭需要專用驅動4.…

原創作品——教育課程界面設計

教育行業UI界面設計需直觀易懂&#xff0c;確保學習者能迅速上手&#xff0c;減少認知負擔。布局清晰&#xff0c;導航便捷&#xff0c;功能按鈕和圖標設計應符合教育場景&#xff0c;直接支持學習目標的達成&#xff0c;促進高效學習體驗。 通過豐富的互動元素&#xff08;如拖…

博途通訊筆記1:1200與1200之間S7通訊

目錄 一、添加子網連接二、創建PUT GET三、各個參數的意義 一、添加子網連接 二、創建PUT GET 三、各個參數的意義

代碼隨想錄(day1)二分法

if語句的基本語法 if 要判斷的條件: 條件成立的時候&#xff0c;要做的事舉例&#xff1a; if nums[middle]<target:leftmiddle1 while語句的基本語法&#xff1a; while 判斷條件(condition)&#xff1a;執行語句(statements)舉例&#xff1a; while left<right:midd…

docker安裝mysql8.0.23

拉取鏡像 docker pull mysql:8.0.23 創建掛載文件 mkdir -p /home/docker/mysql/conf mkdir -p /home/docker/mysql/data mkdir -p /home/docker/mysql/logcd /home/docker/mysql/conf touch my.cnf#授權 chmod 777 -R /home/docker/mysql/conf chmod 777 -R /home/docker/m…

C++類的成員:靜態成員變量、靜態成員函數、非靜態成員變量、非靜態成員函數

(1) 類的成員 A.What&#xff08;什么是類的成員&#xff09; 是組成類的基本構建&#xff0c;包含數據成員、靜態成員和 成員函數 B.Which&#xff08;類的成員有哪些&#xff09; 數據成員&#xff1a; 用于存儲與類的對象相關的數據&#xff0c;例如整數、浮點數、字符串、…

2通道音頻ADC解碼芯片ES7243L、ES7243E、ES7243,用于低成本實現模擬麥克風轉換為IIS數字話筒

前言&#xff1a; 音頻解碼芯片某創參考價格&#xff1a; ES7243L 500&#xff1a;&#xffe5;1.36 / 個 ES7243E 500&#xff1a;&#xffe5;1.66 / 個 ES7243 500&#xff1a; &#xffe5;1.91 / 個 其中ES7243L工作電壓為1.8V&#xff0c;與其他兩款的3.3V工作電壓不同&…

ESP32-C3模組上跑通AES-GCM(2)

接前一篇文章:ESP32-C3模組上跑通AES-GCM(1) 本文內容參考: mbedtls學習筆記 AES GCM_aes128 gcm的aad是什么-CSDN博客 https://www.cnblogs.com/testlearn/p/16547583.html 對稱加密和非對稱加密,一文講解明白!-CSDN博客 深入理解高級加密標準(Advanced Encryption…

日本IT-SIER/SES的區別詳情、契約形態等

一、SLER 主要的服務內容就是“幫客人開發系統或是各種APP&#xff0c;并在指定期間內交貨&#xff0c;交貨后也會持續進行運維等售后服務”。 客人很廣泛&#xff0c;小到普通的服務業商家&#xff08;餐飲店/服飾店/美容業/電商&#xff09;大到各種公共/政府機關&#xff…

【面試題】串聯探針和旁掛探針有什么區別?

在網絡安全領域中&#xff0c;串聯探針和旁掛探針&#xff08;通常也被稱為旁路探針&#xff09;是兩種不同部署方式的監控設備&#xff0c;它們各自具有獨特的特性和應用場景。以下是它們之間的主要區別&#xff1a; 部署方式 串聯探針&#xff1a;串聯探針一般通過網關或者…

`padding`、`border`、`width`、`height` 和 `display` 這些 CSS 屬性的作用

盒模型中的屬性 padding&#xff08;內邊距&#xff09; padding 用于控制元素內容與邊框之間的空間&#xff0c;可以為元素的每個邊&#xff08;上、右、下、左&#xff09;分別設置內邊距。內邊距的單位可以是像素&#xff08;px&#xff09;、百分比&#xff08;%&#xf…

Lambda架構與Kappa架構的特性對比

一個大數據系統架構的設計思想很大程度上受到當時技術條件和思維模式的限制。Lambda架構將批處理層和速度層分為兩層&#xff0c;分別進行離線數據處理和實時數據處理&#xff0c;這樣設計的根本原因在于&#xff0c;Lambda提出的初期是在公司中進行小范圍的業務運用&#xff0…

從Java開發者到.NET Core初級工程師學習路線:C#語言基礎

1. C#語言基礎 1.1 C#語法概覽 歡迎來到C#的世界&#xff01;對于剛從Java轉過來的開發者來說&#xff0c;你會發現C#和Java有很多相似之處&#xff0c;但C#也有其獨特的魅力和強大之處。讓我們一起來探索C#的基本語法&#xff0c;并比較一下與Java的異同。 程序結構 C#程序…

MySQL-數據庫管理:優化、安全、合規與遷移的全面解析

1. 數據庫設計 1.1 需求分析 數據庫設計的第一步是深入理解應用的需求。這通常涉及到與項目團隊&#xff08;包括產品經理、開發者、業務分析師等&#xff09;的緊密合作&#xff0c;以確保數據庫設計能夠準確地反映業務需求。需求分析階段的關鍵活動包括&#xff1a; 收集信…

第一百四十九節 Java數據類型教程 - Java子字符串、字符串轉換

Java數據類型教程 - Java子字符串 獲取子字符串 我們可以使用substring()方法來獲取字符串的子部分。 我們可以將開始索引作為參數&#xff0c;并返回一個從開始索引開始到字符串結尾的子串。 我們還可以將開始索引和結束索引作為參數。 它返回從開始索引開始的子字符串和小…