亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

axios如何利用promise無痛刷新token的實現方法

2019-11-19 10:57:13
字體:
來源:轉載
供稿:網友

需求

最近遇到個需求:前端登錄后,后端返回token和token有效時間,當token過期時要求用舊token去獲取新的token,前端需要做到無痛刷新token,即請求刷新token時要做到用戶無感知。

需求解析

當用戶發起一個請求時,判斷token是否已過期,若已過期則先調refreshToken接口,拿到新的token后再繼續執行之前的請求。
這個問題的難點在于:當同時發起多個請求,而刷新token的接口還沒返回,此時其他請求該如何處理?接下來會循序漸進地分享一下整個過程。

實現思路

由于后端返回了token的有效時間,可以有兩種方法:

方法一:
在請求發起前攔截每個請求,判斷token的有效時間是否已經過期,若已過期,則將請求掛起,先刷新token后再繼續請求。

方法二:
不在請求前攔截,而是攔截返回后的數據。先發起請求,接口返回過期后,先刷新token,再進行一次重試。

兩種方法對比

方法一

  • 優點: 在請求前攔截,能節省請求,省流量。
  • 缺點: 需要后端額外提供一個token過期時間的字段;使用了本地時間判斷,若本地時間被篡改,特別是本地時間比服務器時間慢時,攔截會失敗。

PS:token有效時間建議是時間段,類似緩存的MaxAge,而不要是絕對時間。當服務器和本地時間不一致時,絕對時間會有問題。

方法二

優點:不需額外的token過期字段,不需判斷時間。
缺點: 會消耗多一次請求,耗流量。

綜上,方法一和二優缺點是互補的,方法一有校驗失敗的風險(本地時間被篡改時,當然一般沒有用戶閑的蛋疼去改本地時間的啦),方法二更簡單粗暴,等知道服務器已經過期了再重試一次,只是會耗多一個請求。
在這里博主選擇了 方法二。

實現

這里會使用axios來實現,方法一是請求前攔截,所以會使用axios.interceptors.request.use()這個方法;

而方法二是請求后攔截,所以會使用axios.interceptors.response.use()方法。

封裝axios基本骨架

首先說明一下,項目中的token是存在localStorage中的。request.js基本骨架:

import axios from 'axios'// 從localStorage中獲取tokenfunction getLocalToken () { const token = window.localStorage.getItem('token') return token}// 給實例添加一個setToken方法,用于登錄后將最新token動態添加到header,同時將token保存在localStorage中instance.setToken = (token) => { instance.defaults.headers['X-Token'] = token window.localStorage.setItem('token', token)}// 創建一個axios實例const instance = axios.create({ baseURL: '/api', timeout: 300000, headers: { 'Content-Type': 'application/json', 'X-Token': getLocalToken() // headers塞token }})// 攔截返回的數據instance.interceptors.response.use(response => { // 接下來會在這里進行token過期的邏輯處理 return response}, error => { return Promise.reject(error)})export default instance

這個是項目中一般的axios實例的封裝,創建實例時,將本地已有的token放進header,然后export出去供調用。接下來就是如何攔截返回的數據啦。

instance.interceptors.response.use攔截實現

后端接口一般會有一個約定好的數據結構,如:

{code: 1234, message: 'token過期', data: {}}

如我這里,后端約定當code === 1234時表示token過期了,此時就要求刷新token。

instance.interceptors.response.use(response => { const { code } = response.data if (code === 1234) { // 說明token過期了,刷新token return refreshToken().then(res => {  // 刷新token成功,將最新的token更新到header中,同時保存在localStorage中  const { token } = res.data  instance.setToken(token)  // 獲取當前失敗的請求  const config = response.config  // 重置一下配置  config.headers['X-Token'] = token  config.baseURL = '' // url已經帶上了/api,避免出現/api/api的情況  // 重試當前請求并返回promise  return instance(config) }).catch(res => {  console.error('refreshtoken error =>', res)  //刷新token失敗,神仙也救不了了,跳轉到首頁重新登錄吧  window.location.href = '/' }) } return response}, error => { return Promise.reject(error)})function refreshToken () { // instance是當前request.js中已創建的axios實例 return instance.post('/refreshtoken').then(res => res.data)}

這里需要額外注意的是,response.config就是原請求的配置,但這個是已經處理過了的,config.url已經帶上了baseUrl,因此重試時需要去掉,同時token也是舊的,需要刷新下。

以上就基本做到了無痛刷新token,當token正常時,正常返回,當token已過期,則axios內部進行一次刷新token和重試。對調用者來說,axios內部的刷新token是一個黑盒,是無感知的,因此需求已經做到了。

問題和優化

上面的代碼還是存在一些問題的,沒有考慮到多次請求的問題,因此需要進一步優化。

如何防止多次刷新token

如果refreshToken接口還沒返回,此時再有一個過期的請求進來,上面的代碼就會再一次執行refreshToken,這就會導致多次執行刷新token的接口,因此需要防止這個問題。我們可以在request.js中用一個flag來標記當前是否正在刷新token的狀態,如果正在刷新則不再調用刷新token的接口。

// 是否正在刷新的標記let isRefreshing = falseinstance.interceptors.response.use(response => { const { code } = response.data if (code === 1234) { if (!isRefreshing) {  isRefreshing = true  return refreshToken().then(res => {  const { token } = res.data  instance.setToken(token)  const config = response.config  config.headers['X-Token'] = token  config.baseURL = ''  return instance(config)  }).catch(res => {  console.error('refreshtoken error =>', res)  window.location.href = '/'  }).finally(() => {  isRefreshing = false  }) } } return response}, error => { return Promise.reject(error)})

這樣子就可以避免在刷新token時再進入方法了。但是這種做法是相當于把其他失敗的接口給舍棄了,假如同時發起兩個請求,且幾乎同時返回,第一個請求肯定是進入了refreshToken后再重試,而第二個請求則被丟棄了,仍是返回失敗,所以接下來還得解決其他接口的重試問題。

同時發起兩個或以上的請求時,其他接口如何重試

兩個接口幾乎同時發起和返回,第一個接口會進入刷新token后重試的流程,而第二個接口需要先存起來,然后等刷新token后再重試。同樣,如果同時發起三個請求,此時需要緩存后兩個接口,等刷新token后再重試。由于接口都是異步的,處理起來會有點麻煩。

當第二個過期的請求進來,token正在刷新,我們先將這個請求存到一個數組隊列中,想辦法讓這個請求處于等待中,一直等到刷新token后再逐個重試清空請求隊列。

那么如何做到讓這個請求處于等待中呢?為了解決這個問題,我們得借助Promise。將請求存進隊列中后,同時返回一個Promise,讓這個Promise一直處于Pending狀態(即不調用resolve),此時這個請求就會一直等啊等,只要我們不執行resolve,這個請求就會一直在等待。當刷新請求的接口返回來后,我們再調用resolve,逐個重試。最終代碼:

// 是否正在刷新的標記let isRefreshing = false// 重試隊列,每一項將是一個待執行的函數形式const requests = []instance.interceptors.response.use(response => { const { code } = response.data if (code === 1234) { const config = response.config if (!isRefreshing) {  isRefreshing = true  return refreshToken().then(res => {  const { token } = res.data  instance.setToken(token)  config.headers['X-Token'] = token  config.baseURL = ''  // 已經刷新了token,將所有隊列中的請求進行重試  requests.forEach(cb => cb(token))  return instance(config)  }).catch(res => {  console.error('refreshtoken error =>', res)  window.location.href = '/'  }).finally(() => {  isRefreshing = false  }) } else {  // 正在刷新token,返回一個未執行resolve的promise  return new Promise((resolve) => {  // 將resolve放進隊列,用一個函數形式來保存,等token刷新后直接執行  requests.push((token) => {   config.baseURL = ''   config.headers['X-Token'] = token   resolve(instance(config))  })  }) } } return response}, error => { return Promise.reject(error)})

這里可能比較難理解的是requests這個隊列中保存的是一個函數,這是為了讓resolve不執行,先存起來,等刷新token后更方便調用這個函數使得resolve執行。至此,問題應該都解決了。

最后完整代碼

import axios from 'axios'// 從localStorage中獲取tokenfunction getLocalToken () { const token = window.localStorage.getItem('token') return token}// 給實例添加一個setToken方法,用于登錄后將最新token動態添加到header,同時將token保存在localStorage中instance.setToken = (token) => { instance.defaults.headers['X-Token'] = token window.localStorage.setItem('token', token)}function refreshToken () { // instance是當前request.js中已創建的axios實例 return instance.post('/refreshtoken').then(res => res.data)}// 創建一個axios實例const instance = axios.create({ baseURL: '/api', timeout: 300000, headers: { 'Content-Type': 'application/json', 'X-Token': getLocalToken() // headers塞token }})// 是否正在刷新的標記let isRefreshing = false// 重試隊列,每一項將是一個待執行的函數形式const requests = []instance.interceptors.response.use(response => { const { code } = response.data if (code === 1234) { const config = response.config if (!isRefreshing) {  isRefreshing = true  return refreshToken().then(res => {  const { token } = res.data  instance.setToken(token)  config.headers['X-Token'] = token  config.baseURL = ''  // 已經刷新了token,將所有隊列中的請求進行重試  requests.forEach(cb => cb(token))  return instance(config)  }).catch(res => {  console.error('refreshtoken error =>', res)  window.location.href = '/'  }).finally(() => {  isRefreshing = false  }) } else {  // 正在刷新token,將返回一個未執行resolve的promise  return new Promise((resolve) => {  // 將resolve放進隊列,用一個函數形式來保存,等token刷新后直接執行  requests.push((token) => {   config.baseURL = ''   config.headers['X-Token'] = token   resolve(instance(config))  })  }) } } return response}, error => { return Promise.reject(error)})export default instance

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲韩国青草视频| 久久免费少妇高潮久久精品99| 亚洲国产精品久久久久久| 欧美一性一乱一交一视频| 日韩欧美在线第一页| 精品激情国产视频| 国产精品aaa| 欧美亚洲激情在线| 亚洲va久久久噜噜噜久久天堂| 亚洲字幕在线观看| 亚洲跨种族黑人xxx| 欧美亚洲成人网| 亚洲人a成www在线影院| 91在线色戒在线| 日韩在线中文字幕| 欧美国产第一页| 久久午夜a级毛片| 日本韩国欧美精品大片卡二| 性欧美长视频免费观看不卡| 久久久国产在线视频| 亚洲一区二区三区四区在线播放| 欧美影院成年免费版| 欧美性猛交xxxx| 欧美激情一级欧美精品| 九九精品在线播放| 91精品国产色综合久久不卡98口| 日韩中文字幕久久| 中文字幕亚洲一区二区三区| 久久久精品在线观看| 精品高清美女精品国产区| 国产999在线观看| 国产成人高清激情视频在线观看| 国产成人精品999| 97久久精品在线| 黄色一区二区在线| 日韩国产高清视频在线| 亚洲一区二区免费在线| 一区二区欧美在线| www.日韩不卡电影av| 57pao国产精品一区| 久久久噜噜噜久久久| 国产精品久久久久aaaa九色| 日本久久久久久| 亚洲社区在线观看| 国产日韩在线精品av| 亚洲国产中文字幕久久网| 国产精品高潮呻吟久久av黑人| 亚洲第一精品夜夜躁人人躁| 亚洲国产女人aaa毛片在线| 国产精品成人av性教育| 欧美大成色www永久网站婷| 国产区亚洲区欧美区| 亚洲2020天天堂在线观看| 日韩在线视频国产| 免费av一区二区| 日韩av片电影专区| 欧美日本国产在线| 国产成人亚洲精品| 成人精品视频久久久久| 久久99国产精品久久久久久久久| 菠萝蜜影院一区二区免费| 欧美亚洲免费电影| 国产精品第8页| 亚洲一区二区在线| 精品久久久久久久久国产字幕| 欧美日韩国产黄| 国产成人精品一区| www.日本久久久久com.| 韩国19禁主播vip福利视频| 日本精品视频在线观看| 日韩av中文字幕在线| 欧美午夜精品久久久久久浪潮| 欧美性xxxx极品hd欧美风情| 亚洲精品一区二区三区婷婷月| 中文字幕亚洲综合久久筱田步美| 久久久人成影片一区二区三区观看| 久久免费视频在线观看| 欧美日韩国产成人高清视频| 久久久久久久久久久网站| 亚洲精品一区在线观看香蕉| 欧美在线观看一区二区三区| 亚洲精品电影在线| 国产v综合ⅴ日韩v欧美大片| 91av在线播放| 欧美一性一乱一交一视频| 久久国产视频网站| 欧美自拍视频在线| 久久久人成影片一区二区三区观看| 精品久久久久久电影| 国产福利精品在线| 欧美成人四级hd版| 日韩精品视频免费专区在线播放| 国产视频精品一区二区三区| 欧美一级片免费在线| 欧美性猛交xxx| 91精品国产91久久久久久不卡| 欧美午夜电影在线| 亚洲毛片在线观看.| 欧美视频在线观看免费| 久99九色视频在线观看| 日韩成人小视频| 国内久久久精品| 国产精品成久久久久三级| 韩曰欧美视频免费观看| 亚洲a∨日韩av高清在线观看| www.xxxx精品| 亚洲女性裸体视频| 清纯唯美亚洲激情| 欧美极品美女电影一区| zzijzzij亚洲日本成熟少妇| 亚洲第一视频在线观看| 亚洲欧洲在线观看| 久久国产精品偷| 日韩av网站大全| 少妇高潮久久77777| 国产精品久久久| 国产精品18久久久久久麻辣| 久久久av电影| 黄色一区二区在线| 日韩亚洲欧美中文在线| 日韩av在线免费观看一区| 一个人www欧美| 久久久99久久精品女同性| 国产日韩欧美日韩| 欧美一级视频一区二区| …久久精品99久久香蕉国产| 日韩欧美大尺度| 亚洲国产美女精品久久久久∴| 日韩免费观看av| 亚洲欧美在线x视频| 国产成人精品视频在线观看| 日韩精品久久久久久久玫瑰园| 国产福利精品在线| 欧美激情第三页| 亚洲一区二区三区在线视频| 午夜免费久久久久| 亚洲综合av影视| 91色中文字幕| 欧美猛交免费看| 91经典在线视频| 欧美猛交免费看| 国产精品自拍网| 国产精品久久久久不卡| 欧美成在线视频| 国产精品爱啪在线线免费观看| 欧美性高跟鞋xxxxhd| 免费97视频在线精品国自产拍| 一区三区二区视频| 亚洲情综合五月天| 国产精品一久久香蕉国产线看观看| 国产亚洲精品久久久久久牛牛| 国产精品视频不卡| 久久久在线免费观看| 欧美大片第1页| 欧美日韩黄色大片| 亚洲第一免费网站| 日韩av网址在线观看| 欧美激情中文字幕在线| 清纯唯美日韩制服另类| 欧美精品电影免费在线观看| 久久久久99精品久久久久| 91久久精品美女高潮| 日韩精品在线免费观看视频| 日韩网站免费观看|