如何处理 axios 中的 401(身份验证错误)并做出反应? [英] How to handle 401 (Authentication Error) in axios and react?

查看:77
本文介绍了如何处理 axios 中的 401(身份验证错误)并做出反应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个文件 request.js,其中包含 axios ajax 请求的包装器.我正在从多个反应组件调用请求函数,当其中一个请求失败时,我想刷新令牌并再次重试所有失败的请求.我可以使用拦截器,但我不知道如何实现它.请帮忙.

I have one file request.js which contains wrapper for axios ajax request. I am calling request function from multiple react components and when one of the request fails I want to refresh the token and retry all the failed requests again. I can use intercepters, but I don't know how to implement it. Please help.

请求.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;

它是 2019 年,这是另一个实现.上面的解决方案很好,但不能很好地处理多个失败的请求,反过来它也用更新的令牌调用 getToken.

Its 2019, Here is yet another implementation for the same. The above solution is great but does not work well with multiple failed request, in turn it calls getToken with the updated token as well.

 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;

这篇关于如何处理 axios 中的 401(身份验证错误)并做出反应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆