axiosとreactで401(認証エラー)を処理するには?
2023-10-03 21:55:21
質問
私はaxios ajaxリクエストのラッパーを含む1つのファイルrequest.jsを持っています。私は複数の反応コンポーネントからリクエスト関数を呼び出しており、リクエストの1つが失敗したとき、私はトークンをリフレッシュし、失敗したすべてのリクエストを再試行したいです。私はインターセプターを使用することができますが、私はそれを実装する方法がわかりません。助けてください。
request.js
var client = axios.create({
baseURL: 'http://192.168.1.3:3000',
headers: {
appID: 8,
version: "1.1.0",
empID: localStorage.getItem('empID'),
token: localStorage.getItem('accessToken')
}
});
const request = function(options) {
const onSuccess = function(response) {
console.debug('Request Successful!', response);
return response.data;
}
const onError = function(error) {
console.error('Request Failed:', error.config);
if (error.response) {
console.error('Status:', error.response.status);
console.error('Data:', error.response.data);
console.error('Headers:', error.response.headers);
} else {
console.error('Error Message:', error.message);
}
return Promise.reject(error.response || error.message);
}
return client(options)
.then(onSuccess)
.catch(onError);
options
}
export default request;
どのように解決するのですか?
私は、以下のコードで動作するようになりました。
import axios from 'axios';
import config from '../../configuration.json';
import qs from 'qs';
const baseURL = config['baseUrl_local'];
let authTokenRequest;
/**
* @description axios instance for ajax requests
*/
var client = axios.create({
baseURL: baseURL,
headers: {
appID: 8,
version: "1.1.0",
empID: localStorage.getItem('empID'),
token: localStorage.getItem('accessToken')
}
});
/**
* @description this method calls a requestNewToken method to issue a
new token to the client
*/
function getAuthToken() {
if (!authTokenRequest) {
authTokenRequest = requestNewToken();
authTokenRequest.then(resetAuthTokenRequest, resetAuthTokenRequest);
}
return authTokenRequest;
}
/**
* @description this method requests the server to issue a new token,
the server response is updated in local storage accessToken
*/
function requestNewToken() {
var newToken = request({
method: "post",
url: '/sign-in',
data: qs.stringify({
"userName":localStorage.getItem('userName'),
"password":localStorage.getItem('password')
})
}).then((res)=>{
if(res.status == "success"){
localStorage.setItem('accessToken',res.data.accessToken);
//if featureArray is present in response object, update the
featureArray in local storage
if(res.data.features){
localStorage.setItem(
'featureArray',
JSON.stringify(res.data.features));
}
client = axios.create({
baseURL: baseURL,
headers: {
appID: 8,
version: "1.1.0",
empID: localStorage.getItem('empID'),
token: localStorage.getItem('accessToken')
}
});
} else {
window.location = "/logout";
}
});
return newToken;
}
function resetAuthTokenRequest() {
authTokenRequest = null;
}
/**
* @description if any of the API gets 401 status code, this method
calls getAuthToken method to renew accessToken
* updates the error configuration and retries all failed requests
again
*/
client.interceptors.response.use(undefined, err => {
const error = err.response;
// if error is 401
if (error.status===401 && error.config &&
!error.config.__isRetryRequest) {
// request for a new token
return getAuthToken().then(response => {
// update the error config with new token
error.config.__isRetryRequest = true;
error.config.headers.token= localStorage.getItem("accessToken");
return client(error.config);
});
}
});
/**
* @description wrapper for making ajax requests
* @param {object} object with method,url,data etc.
*/
const request = function(options) {
const onSuccess = function(response) {
return response.data;
}
const onError = function(error) {
//console.error('Request Failed:', error.config);
if (error.response) {
//console.error('Status:', error.response.status);
//console.error('Data:', error.response.data);
//console.error('Headers:', error.response.headers);
} else {
console.error('Error Message:', error.message);
}
return Promise.reject(error.response || error.message);
}
return client(options)
.then(onSuccess)
.catch(onError);
options
}
export default request;
[EDIT] その2019、ここに同じことのためのさらに別の実装があります。上記の解決策は素晴らしいですが、複数の失敗したリクエストではうまく動作しません、順番に、更新されたトークンでgetTokenを同様に呼び出します。
import axios from "axios";
/* @internal */
import config from "../config";
import TokenService from "./token_service";
class Request {
constructor() {
this.baseURL = config.baseUrl;
this.isRefreshing = false;
this.failedRequests = [];
this.tokenService = new TokenService();
this.client = axios.create({
baseURL: config.apiServerBaseUrl,
headers: {
clientSecret: this.clientSecret,
},
});
this.beforeRequest = this.beforeRequest.bind(this);
this.onRequestFailure = this.onRequestFailure.bind(this);
this.processQueue = this.processQueue.bind(this);
this.client.interceptors.request.use(this.beforeRequest);
this.client.interceptors.response.use(this.onRequestSuccess,
this.onRequestFailure);
}
beforeRequest(request) {
const token = TokenService.getAccessToken();
request.headers.Authorization = `Token ${token}`;
return request;
}
static onRequestSuccess(response) {
return response.data;
}
async onRequestFailure(err) {
const { response } = err;
if (response.status === 401 && err && err.config && !err.config.__isRetryRequest) {
if (this.isRefreshing) {
try {
const token = await new Promise((resolve, reject) => {
this.failedRequests.push({ resolve, reject });
});
err.config.headers.Authorization = `Bearer ${token}`;
return this.client(err.config);
}
catch (e) {
return e;
}
}
this.isRefreshing = true;
err.config.__isRetryRequest = true;
return new Promise((resolve, reject) => {
this.tokenService.refreshAccessToken().then((token) => {
this.tokenService.setAccessToken(token);
err.config.headers.Authorization = `Bearer ${token}`;
this.isRefreshing = false;
this.processQueue(null, token);
resolve(this.client(err.config));
}).catch((e) => {
this.processQueue(e, null);
reject(err.response);
});
});
}
throw response;
}
processQueue(error, token = null) {
this.failedRequests.forEach((prom) => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
this.failedRequests = [];
}
}
const request = new Request();
export default request.client;
関連
-
[解決済み] Node.jsを完全にアンインストールして、最初から再インストールする方法 (Mac OS X)
-
[解決済み] Node.jsのmodule.exportsの目的と使い方を教えてください。
-
[解決済み] React NativeとReactの違いは何ですか?
-
[解決済み] Reactコンポーネントに条件付きで属性を追加するにはどうすればよいですか?
-
[解決済み] Reactのstateとpropsの違いとは?
-
[解決済み] AxiosのHTTPエラーからステータスコードを取得するにはどうすればよいですか?
-
[解決済み】JavaScriptで2つの配列を結合し、項目の重複を排除する方法
-
[解決済み] WebStormで未解決の変数が大量にある場合の警告に対処する方法は?
-
[解決済み] node.jsで文字列のsha1ハッシュを取得するにはどうすればよいですか?
-
[解決済み] Reactメモを使うべきではない場合とは?
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Javascriptによるタッチスクリーンデバイスの検出
-
[解決済み] Node.jsでbase64エンコードされた画像をAmazon S3へアップロードする
-
[解決済み] ExtJS 4のイベントハンドリングについて
-
[解決済み] オブジェクトの配列からReactコンポーネントをレンダリングする
-
[解決済み] JavaScriptで文字列を数値に変換する最速の方法は何ですか?
-
[解決済み] BlobからArrayBufferへ移行する方法
-
[解決済み] javascriptで文字列から関数を作成する方法はありますか?
-
[解決済み] ECMAScriptとは?
-
[解決済み] JavaScriptのArray.sort()メソッドでシャッフルするのは正しいのか?
-
[解決済み] Fetch: ステータスがOKでない場合、プロミスを拒否し、エラーをキャッチするか?