配置請求地址:config->index.js 一個項目里通常有一個config->index.js,該文件包含了當前項目的請求地址,以及項目的版本信息。 // 請求地址 const API_URL_DEV = 'http://xxx.xxx.xxx.net:81/xxx' // 測試介面 cons ...
配置請求地址:config->index.js
一個項目里通常有一個config->index.js
,該文件包含了當前項目的請求地址,以及項目的版本信息。
// 請求地址
const API_URL_DEV = 'http://xxx.xxx.xxx.net:81/xxx' // 測試介面
const API_URL_TRIAL = 'http://xxx.xxx.xxx.net:81/xxx' // 體驗版介面
const API_URL_PROD = 'http://xxx.xxx.xxx.net:81/xxx' // 線上介面
上面就是api
請求的baseUrl
,在後續配置axios
的時候會用到,在項目開發中,有時候會切換開發版和體驗版,就是更改此處的地址。
微信小程式里需要區分一件事,那就是當前的運行環境,是否是開發環境。
微信小程式官方提供了 wx.getAccountInfoSync()
可以獲取小程式版本的appid
以及小程式的版本信息,根據envVersion
來判斷當前小程式是線下環境還是線上環境。
let {
miniProgram: {
envVersion,
version
}
} = wx.getAccountInfoSync() // 獲取小程式版本信息
將獲取版本信息的操作封裝成一個函數,根據運行環境配置小程式版本信息和apiUrl
function getparamsByEnv() {
/**
* 官方提供:wx.getAccountInfoSync()獲取小程式版本信息,以及appid
* 模擬當前環境
* 線下的是 'develop' 線上是 'prod'
*/
let {
miniProgram: {
envVersion,
version
}
} = wx.getAccountInfoSync() // 獲取小程式版本信息
// 返回的參數
let params = {
apiUrl: '',
version: version,
tester: true
}
switch (envVersion) {
case 'develop':
params.apiUrl = API_URL_DEV // 此處對應上面配置的線下baseUrl介面
params.version = '1.0.2'
break
case 'trial':
params.apiUrl = API_URL_TRIAL // 此處對應上面配置的體驗版baseUrl介面
params.version = '1.0.2'
break
default:
params.apiUrl = API_URL_PROD // 此處對應上面配置的線上baseUrl介面
params.tester = false
break
}
return params // 配置完成後返回params
}
完整的config.js
配置
// 請求地址
const API_URL_DEV = 'http://xxx.xxx.xxx.net:81/xxx' // 測試介面
const API_URL_TRIAL = 'http://xxx.xxx.xxx.net:81/xxx' // 體驗版介面
const API_URL_PROD = 'http://xxx.xxx.xxx.net:81/xxx' // 線上介面
const envParams = getparamsByEnv()
export default envParams.apiUrl // 導出apiUrl
export const version = envParams.version // 導出版本信息
export const tester = envParams.tester // 導出tester
/**
* 區分小程式當前運行環境
* @return {Boolean} 是否是開發環境
*/
function getparamsByEnv() {
/**
* 官方提供:wx.getAccountInfoSync()獲取小程式版本信息,以及appid
* 模擬當前環境
* 線下的是 'develop' 線上是 'prod'
*/
let {
miniProgram: {
envVersion,
version
}
} = wx.getAccountInfoSync() // 獲取小程式版本信息
let params = {
apiUrl: '',
version: version,
tester: true
}
switch (envVersion) {
case 'develop':
params.apiUrl = API_URL_DEV
params.version = '1.0.2'
break
case 'trial':
params.apiUrl = API_URL_TRIAL
params.version = '1.0.2'
break
default:
params.apiUrl = API_URL_PROD
params.tester = false
break
}
return params
}
配置request.js
在這個文件其實就是配置axios
,需要引入axios
以及上面配置的config.js
文件
import axios from 'axios'
import mpAdapter from 'axios-miniprogram-adapter'
axios.defaults.adapter = mpAdapter
import API_URL from '../config/index' // apiUrl
axios
有三個配置項,分別是:baseUrl
、請求攔截器
、響應攔截器
,下麵分別就這些配置項進行配置。
配置請求baseUrl
引入config.js
的apiUrl
,在axios
的baseUrl
中配置
const service = axios.create({
baseURL: API_URL, // url = base url + request url
})
配置請求攔截器:service.interceptors.request.use
在請求攔截器內通常會做一些配置,比如:在請求頭中攜帶token
,在請求的時候彈出“載入中”告訴用戶正在發生什麼等等,通常只要發送請求,都會經過請求攔截器。
let ajaxTimes = 0; // 狀態
// 請求攔截器
service.interceptors.request.use(function (config) {
// config就是請求的配置信息,裡面包含baseUrl地址,headers請求頭,url請求介面等信息
// 在發送請求前,可以對config做一些配置操作(加鹽)
// 獲取token
if(wx.getStorageSync('token')){
// 如果token存在,那麼在請求頭上帶上token
config.headers['access_token'] = wx.getStorageSync('token')
}
// 請求的時候,添加一個彈框,告訴用戶正在發生什麼
// config.url != 'xxx/xxx' 請求某個介面不彈出提示
if (!ajaxTimes++ && config.url != '/lottery/activity/get-prize'){
wx.showLoading({ title: '載入中···' }) // 其實把wx.showLoading寫在請求攔截器里都會生效,這裡只是做了一個ajaxTimes的判斷,優化了提示彈框
}
return config; // 返回配置信息-也就是請求頭之類的
},function (error){
// 處理錯誤請求
return Promise.reject(error)
})
配置響應攔截器:service.interceptors.response.use
只要請求介面,介面響應並返回,都會走響應攔截器,可以在響應攔截器內做一些配置,比如請求日誌、token
過期處理、賬戶封禁處理、伺服器錯誤處理、響應成功處理等等。
// 添加響應攔截器
service.interceptors.response.use(function (response) {
// response返回上方請求攔截器的config配置信息,data後臺介面返回的數據,headers伺服器響應頭等信息
// 由於微信小程式的 toast 和 loading 相關介面可以相互混用,所以需要取消混用提示,也是關聯上方的“載入中”優化
if (ajaxTimes > 0 && --ajaxTimes === 0) {
wx.hideLoading({noConflict:true}) // // 取消混用提示
}
// 這裡返回的response.data就是後臺介面返回的數據
const res = response.data
// 如果返回的code不是200或201,則判斷為錯誤
if(res.code && res.code !== 200 && res.code !== 201){
// 分別對這些錯誤進行對應的處理
if(res.code === 1004){
// 1004狀態碼表示token過期,需要重新獲取token,併在獲取token後,重新請求介面,清除token
return reSetToken(response.config) // 這裡調用獲取token的方法,後面會進行配置
} else if(res.code === 9001){
// 返回9001狀態碼處理,通過wx.login獲取code
if (res.message.indexOf('code been used') !== -1) {
getApp().globalData.userInfo._getLoginCode()
} else if (res.message.indexOf('invalid code') !== -1) {
console.log('invalid code')
}
return Promise.reject(res)
} else if (res.code === 400 || res.code === 500){
// 返回400或500狀態碼,表示伺服器錯誤或者介面請求異常
wx.showToast({title: `${res.message}`,icon: 'none'})
return Promise.reject(res)
} else if(res.code == 30011){ // 賬戶封禁
// 返回30011狀態碼,表示該賬號已封禁,如果想對封禁的賬戶做一些操作可以將返回的信息攜帶並跳轉到封禁頁,封禁頁展示封禁的原有和信息,用戶可以在封禁頁做封禁申訴等操作
const info = res.message;
wx.reLaunch({
url: `/pages/account/index?info=${info}`,
})
}
return Promise.reject(res) // 其他的錯誤狀態碼就直接reject
} else {
return res
}
}, function (error) {
// 處理響應錯誤
if (ajaxTimes > 0 && --ajaxTimes === 0) {
wx.hideLoading({noConflict:true})
}
return Promise.reject(error);
}
token
過期後重新獲取token
,在響應攔截器里說過,返回狀態1004
表示token
過期,在這裡封裝重新獲取token
的方法,在響應攔截器狀態1004
處調用。
let tokenLoad = false
// 重新獲取token
function reSetToken(params) {
// 這裡的params就是response.config的信息
return new Promise(async (resolve,reject) => {
// 用函數形式將resolve存入,等待刷新後再執行
const app = getApp() // 獲取小程式全局唯一的 App 實例
if(!app){
return console.log("app undefined")
}
wx.queue.asyncWait("login_back", () => {
service(params).then(res => {
resolve(res)
}, err => {
reject(err)
})
},params.url)
// tokenLoad = true什麼都不做,false走重新發起登錄的流程
if(tokenLoad) {
console.log("中斷重新獲取token")
} else {
// 將tokenLoad狀態更改為true
tokenLoad = true
try {
// 重新發起登錄,調用發起登錄的方法
app.globalData.userInfo.Login().then(res => {
if(res === 'notBindPhone') {
app.awaitLoginDialog('request')
}
tokenLoad = false // 登錄成功後將tokenLoad改為false,等待下次token過期重新發起登錄
}).catch(()=>{
tokenLoad = false
})
} catch (error) {
// 重新獲取token報錯,說明用戶沒註冊,需要喚醒註冊彈窗,如果在tabbar頁面,就不自動喚醒
app.awaitLoginDialog('request')
tokenLoad = false
reject(error)
}
}
})
}
完整的request.js
配置
import axios from 'axios'
import mpAdapter from 'axios-miniprogram-adapter'
axios.defaults.adapter = mpAdapter
import API_URL from '../config/index'
const service = axios.create({
baseURL: API_URL, // url = base url + request url
})
let ajaxTimes = 0
// 請求攔截器
service.interceptors.request.use(function (config) {
// config是請求的配置信息,內部有baseUrl地址,headers請求頭,url請求介面等信息
// console.log('請求攔截器',config);
// 發送請求之前你可以在這裡對config做一些配置
// 獲取token
if (wx.getStorageSync('token')) {
// 如果token存在,那麼在請求頭上帶上token
config.headers['access_token'] = wx.getStorageSync('token')
}
// 請求的時候,添加一個彈框wx.showLoading告訴用戶正在載入中
// !ajaxTimes++是因為請求可能不止一次,請求的時候這種"載入中"的提醒優化,其實只需要顯示一次即可,所以需要做一個判斷,ajaxTimes == 0的時候提示一次,後續ajaxTimes++都不會提示,每次進新頁面或刷新都會重置ajaxTimes == 0 ,這裡做的操作其實就是為了優化,減少提示的次數。
if (!ajaxTimes++ && config.url != '/lottery/activity/get-prize' && config.url != '/lottery/activity/time'){
wx.showLoading({ title: '載入中···' }) // 其實把wx.showLoading寫在請求攔截器里都會生效,這裡只是做了一個ajaxTimes的判斷,優化了提示彈框
}
return config;
}, function (error) {
// 處理請求錯誤
return Promise.reject(error);
});
// 添加一個響應攔截器
service.interceptors.response.use(function (response) {
// response返回請求攔截器的config配置信息,data後臺介面返回的數據,headers伺服器響應頭等信息
// console.log('響應攔截器',response);
if (ajaxTimes > 0 && --ajaxTimes === 0) {
wx.hideLoading({noConflict:true})
}
// 這裡返回的response.data就是後臺介面返回的數據
const res = response.data
console.log('請求日誌:',response); // 請求日誌
// 如果自定義代碼不是200或201,則判斷為錯誤.
if (res.code && res.code !== 200 && res.code !== 201 && res.code !== 2001 && res.code !== 2002) {
if (res.code === 1004) {
// 1004狀態碼表示token過期,這是需要重新獲取token,並且在獲取完token之後,重新請求介面,清除token
return reSetToken(response.config)
} else if (res.code === 9001) {
if (res.message.indexOf('code been used') !== -1) {
getApp().globalData.userInfo._getLoginCode()
} else if (res.message.indexOf('invalid code') !== -1) {
console.log('invalid code')
}
return Promise.reject(res)
} else if (res.code === 400 || res.code === 500) {
wx.showToast({title: `${res.message}`,icon: 'none'})
return Promise.reject(res)
} else if (res.code === 30011) { // 賬戶封禁
const info = res.message;
// 根據code判斷該賬戶是否封禁,如果封禁,則攜帶信息跳轉到封禁頁
wx.reLaunch({
url: `/pages/account/index?info=${info}`,
})
}
return Promise.reject(res)
} else {
return res
}
}, function (error) {
// 處理響應錯誤
if (ajaxTimes > 0 && --ajaxTimes === 0) {
wx.hideLoading({noConflict:true})
}
return Promise.reject(error);
});
let tokenLoad = false
// 重新獲取token
function reSetToken(params) {
// console.log('重新獲取token:',params);
return new Promise(async (resolve, reject) => {
// 用函數形式將 resolve 存入,等待刷新後再執行
const app = getApp()
if (!app) {
console.error("app undefind");
return
}
wx.queue.asyncWait("login_back", () => {
service(params).then(res => {
resolve(res)
}, err => {
reject(err)
})
},params.url)
// true什麼都不做,false走重新發起登錄流程
if (tokenLoad) {
console.log("中斷重新獲取token");
} else {
// 將tokenLoad狀態更改為true
tokenLoad = true
try {
// 重新發起登錄
app.globalData.userInfo.Login().then(res => {
// console.log('發起登錄',res);
if (res === 'notBindPhone') {
app.awaitLoginDialog('request')
}
tokenLoad = false // 登錄成功後將tokenLoad改為false,等待下次token過期重新發起登錄
}).catch(() => {
tokenLoad = false
})
} catch (error) {
// 重新獲取token報錯,說明用戶沒有註冊。需要喚起註冊彈窗,如果是在tabbar頁面,就不自動喚起
// console.log(error, '請求token失敗報錯')
app.awaitLoginDialog('request')
tokenLoad = false
reject(error)
}
}
})
}
export default service
getApp()
為微信小程式的全局唯一App
實例,用戶的登錄方法可以掛載到App
實例當中,在需要喚醒登錄處獲取getApp()
中掛載的登錄方法即可喚醒登錄。