Axiosインターセプターは元のリクエストを再試行し、元のプロミスにアクセスします。
質問
アクセストークンが期限切れの場合、401エラーをキャッチするためにインターセプターを設置しています。有効期限が切れた場合、新しいアクセストークンを取得するためにリフレッシュトークンを試します。この間、他の呼び出しが行われた場合、アクセストークンが検証されるまでキューに入れられます。
これはすべて非常にうまく動作しています。しかし、Axios(originalRequest)を使用してキューを処理するとき、もともと添付されたプロミスは呼び出されません。例として、以下を参照してください。
動作するインターセプターのコードです。
Axios.interceptors.response.use(
response => response,
(error) => {
const status = error.response ? error.response.status : null
const originalRequest = error.config
if (status === 401) {
if (!store.state.auth.isRefreshing) {
store.dispatch('auth/refresh')
}
const retryOrigReq = store.dispatch('auth/subscribe', token => {
originalRequest.headers['Authorization'] = 'Bearer ' + token
Axios(originalRequest)
})
return retryOrigReq
} else {
return Promise.reject(error)
}
}
)
リフレッシュ方法(リフレッシュトークンを使って、新しいアクセストークンを取得する)
refresh ({ commit }) {
commit(types.REFRESHING, true)
Vue.$http.post('/login/refresh', {
refresh_token: store.getters['auth/refreshToken']
}).then(response => {
if (response.status === 401) {
store.dispatch('auth/reset')
store.dispatch('app/error', 'You have been logged out.')
} else {
commit(types.AUTH, {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token
})
store.dispatch('auth/refreshed', response.data.access_token)
}
}).catch(() => {
store.dispatch('auth/reset')
store.dispatch('app/error', 'You have been logged out.')
})
},
auth/actionsモジュールのSubscribeメソッド。
subscribe ({ commit }, request) {
commit(types.SUBSCRIBEREFRESH, request)
return request
},
Mutationと同様に
[SUBSCRIBEREFRESH] (state, request) {
state.refreshSubscribers.push(request)
},
以下はアクションの例です。
Vue.$http.get('/users/' + rootState.auth.user.id + '/tasks').then(response => {
if (response && response.data) {
commit(types.NOTIFICATIONS, response.data || [])
}
})
このリクエストがキューに追加された場合、リフレッシュトークンが新しいトークンにアクセスしなければならないので、私はオリジナルのthen()を添付したいと思います。
const retryOrigReq = store.dispatch('auth/subscribe', token => {
originalRequest.headers['Authorization'] = 'Bearer ' + token
// I would like to attache the original .then() as it contained critical functions to be called after the request was completed. Usually mutating a store etc...
Axios(originalRequest).then(//if then present attache here)
})
アクセストークンがリフレッシュされると、リクエストのキューが処理されます。
refreshed ({ commit }, token) {
commit(types.REFRESHING, false)
store.state.auth.refreshSubscribers.map(cb => cb(token))
commit(types.CLEARSUBSCRIBERS)
},
どのように解決するのですか?
2019年2月13日更新
このトピックに多くの人が興味を示しているので、私は、このトピックのために axios-auth-refresh パッケージを作成しました。 を作成しました。これは、ここで指定された動作を実現するのに役立つはずです。
ここで重要なのは、正しいPromiseオブジェクトを返すことであり、そのために
.then()
でチェーニングすることです。そのためにVuexのステートを使うことができます。リフレッシュの呼び出しが発生した場合、その時点で
refreshing
の状態を
true
に変更すれば、保留中の呼び出しに更新を設定することもできます。このように
.then()
を使用すると、常に正しいPromiseオブジェクトにバインドされ、Promiseが完了したときに実行されます。このようにすることで、トークンの更新を待っている呼び出しを保持するための余分なキューが不要になります。
function refreshToken(store) {
if (store.state.auth.isRefreshing) {
return store.state.auth.refreshingCall;
}
store.commit('auth/setRefreshingState', true);
const refreshingCall = Axios.get('get token').then(({ data: { token } }) => {
store.commit('auth/setToken', token)
store.commit('auth/setRefreshingState', false);
store.commit('auth/setRefreshingCall', undefined);
return Promise.resolve(true);
});
store.commit('auth/setRefreshingCall', refreshingCall);
return refreshingCall;
}
これは常に、すでに作成されたリクエストをPromiseとして返すか、新しいリクエストを作成し、他の呼び出しのためにそれを保存するかのどちらかでしょう。これで、あなたのインターセプターは次のようなものになるでしょう。
Axios.interceptors.response.use(response => response, error => {
const status = error.response ? error.response.status : null
if (status === 401) {
return refreshToken(store).then(_ => {
error.config.headers['Authorization'] = 'Bearer ' + store.state.auth.token;
error.config.baseURL = undefined;
return Axios.request(error.config);
});
}
return Promise.reject(error);
});
これにより、保留されているすべてのリクエストをもう一度実行することができます。しかし、クエリなしですべて一度に実行されます。
保留中のリクエストを実際に呼び出された順に実行させたい場合は、コールバックを第二引数として
refreshToken()
関数の2番目のパラメータとして渡す必要があります。
function refreshToken(store, cb) {
if (store.state.auth.isRefreshing) {
const chained = store.state.auth.refreshingCall.then(cb);
store.commit('auth/setRefreshingCall', chained);
return chained;
}
store.commit('auth/setRefreshingState', true);
const refreshingCall = Axios.get('get token').then(({ data: { token } }) => {
store.commit('auth/setToken', token)
store.commit('auth/setRefreshingState', false);
store.commit('auth/setRefreshingCall', undefined);
return Promise.resolve(token);
}).then(cb);
store.commit('auth/setRefreshingCall', refreshingCall);
return refreshingCall;
}
そして、インターセプター。
Axios.interceptors.response.use(response => response, error => {
const status = error.response ? error.response.status : null
if (status === 401) {
return refreshToken(store, _ => {
error.config.headers['Authorization'] = 'Bearer ' + store.state.auth.token;
error.config.baseURL = undefined;
return Axios.request(error.config);
});
}
return Promise.reject(error);
});
2番目の例はまだテストしていませんが、うまくいくか、少なくともアイデアを与えてくれるはずです。
最初の例の動作デモ - モックリクエストとそれに使われるサービスのデモ版のため、しばらくすると動かなくなりますが、それでもコードは存在します。
関連
-
[解決済み] .then()チェーンで以前のプロミス結果にアクセスするにはどうすればよいですか?
-
[解決済み] Google maps API V3 - 同一地点に複数のマーカーを設置する。
-
[解決済み] URL/アドレスバーからJavascriptの関数を呼び出す
-
[解決済み] JavaScriptで:hoverのCSSプロパティを変更する
-
[解決済み] JSXとLoadshを使用して、ある要素をn回繰り返す方法
-
[解決済み] 無効になっている入力フィールドの値を送信する
-
[解決済み] Javascriptで動的に命名されたメソッドを呼び出すにはどうすればよいですか?
-
[解決済み] TypeScriptプロジェクトで既存のC#クラス定義を再利用する方法
-
[解決済み] JSHintの'+'前の改行不良の説明
-
[解決済み] JavaScriptのArray.sort()メソッドでシャッフルするのは正しいのか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] チェックボックスが選択されているかどうかを確認するjQuery
-
[解決済み] なぜJavaScriptでは!{}[true]がtrueに評価されるのですか?
-
[解決済み] Google maps API V3 - 同一地点に複数のマーカーを設置する。
-
[解決済み] bootstrap のポップオーバーがすべての要素の上に表示されない
-
[解決済み] JSXとLoadshを使用して、ある要素をn回繰り返す方法
-
[解決済み] JavaScriptで文字列を数値に変換する最速の方法は何ですか?
-
[解決済み] jQueryの$という記号の意味は何ですか?
-
[解決済み] ECMAScriptとは?
-
[解決済み] Prototypeを使ってtextareaを自動サイズ調整するには?
-
[解決済み] JavaScriptとLuaの微妙な違い [終了しました]