【问题标题】:Axios multiple request on interceptor拦截器上的 axios 多个请求
【发布时间】:2019-01-17 17:16:50
【问题描述】:

我在我的 react 应用中使用库 axios。
我的拦截器有问题。

我的问题是假设我有三个请求同时发生并且我没有令牌,拦截器调用getUserRandomToken 三次,我希望拦截器会等到我从第一个请求中获取令牌然后继续给其他人。

附言他的令牌有一个到期日期,所以我也会检查它,如果到期日期无效,我需要创建一个新令牌。

这是拦截器:

axios.interceptors.request.use(
  config => {
    /*I'm getting the token from the local storage
    If there is any add it to the header for each request*/
    if (tokenExist()) {
      config.headers.common["token"] = "...";
      return config;
    }
    /*If there is no token i need to generate it
     every time create a random token, this is a axios get request*/
    getUserRandomToken()
      .then(res => {
        /*add the token to the header*/
        config.headers.common["token"] = res;
        return config;
      })
      .catch(err => {
        console.log(err);
      });
  },
  function(error) {
    // Do something with request error
    return Promise.reject(error);
  }
);

【问题讨论】:

  • 您可以在某个全局上下文(本地存储)中创建一个标志,该标志将保持“正在生成令牌”的状态,并使用它来告诉其他请求等到它完成
  • 您还可以创建一个处理令牌生成的单例对象。所有调用都会向同一个实例请求令牌。该单身人士将负责第一次生成新令牌并在后续请求中返回它
  • @OriPrice 我试图用一个标志来做,但不幸的是,它并不顺利,因为对令牌的请求只发生了一次,但拦截器中的另一个请求没有停止,所以我已经得到了来自他们的响应 401(未经授权)。

标签: javascript asynchronous concurrency axios race-condition


【解决方案1】:

处理令牌生成的单例对象怎么样?类似的东西:

const tokenGenerator ={
  getTokenPromise: null,
  token: null,
  getToken(){
    if (!this.getTokenPromise){
      this.getTokenPromise = new Promise(resolve=>{
        /*supposed to be a http request*/
        if (!this.token){
          setTimeout(()=>{
            this.token = 'generated';
            resolve(this.token);
          },0)
        }else{
          resolve(this.token);
        }
      })
    }
    return this.getTokenPromise;
  }

您可以从拦截器中引用同一个对象。

参见示例:JS FIddle 参考:reference

【讨论】:

  • 这看起来不错,但是当我收到多个请求时它仍然无法解决问题,我怎样才能让拦截器等待所有请求上的令牌,除了发生的第一个请求?跨度>
  • 这个例子就是这样做的,它创建了 3 个异步调用。第一个到达生成令牌将首先完成生成它,然后再进行其他调用。只要您保留相同的 tokenGenerator 实例
  • 我尝试了类似的方法,但是我请求新令牌的请求是异步的,因此其他请求不会等待,它们会在拦截器中继续
  • 更新小提琴并使获取令牌异步。
  • @OriPrice 你是英雄。工作正常
【解决方案2】:

您可以从拦截器回调返回一个 Promise 以“等待”直到 Promise 完整文件(这将适合您的情况)。看看这个例子:

function axiosCall () {
  return new Promise((resolve, reject) => {
    Axios.post(URL, {apiKey}).then((response) => {
      resolve(response.data.message);
    }).catch((error) => {
      reject(error);
    });
  });
}

instance.interceptors.request.use((config) => {
  return axiosCall().then((tokenResponse) => {
    setWebCreds(tokenResponse);
    config.headers.Authorization = `Bearer ${tokenResponse}`;
    return Promise.resolve(config)
  }).catch(error => {
    // decide what to do if you can't get your token
  })
}, (error) => {
  return Promise.reject(error);
});

更多详情:https://github.com/axios/axios/issues/754

【讨论】:

    【解决方案3】:

    按照代码执行某些任务:

    1. 在 401 上更新令牌
    2. 在令牌刷新时创建一个失败请求队列。
    3. 令牌刷新后恢复原始请求。
    4. 一旦给定特殊请求 200,将其从队列中删除。

    Config.js

    import axios from 'axios';
    import { AsyncStorage } from 'react-native';
    import { stateFunctions } from '../../src/sharedcomponent/static';
    
    const APIKit = axios.create({
        baseURL: '',
        timeout: 10000,
        withCredentials: true,
    });
    const requestArray = [];
    // Interceptor for Request
    export const setClientToken = token => {
        APIKit.interceptors.request.use(
            async config => {
                console.log('Interceptor calling');
                let userToken = await AsyncStorage.getItem('userToken');
                userToken = JSON.parse(userToken);
                config.headers = {
                    'Authorization': `Bearer ${userToken}`,
                    'Accept': 'application/json',
                    "Content-Type": "application/json",
                    "Cache-Control": "no-cache",
                }
                //   console.log('caling ' , config)
                return config;
            },
            error => {
                Promise.reject(error)
            });
    };
    
    
    // Interceptor for Response
    APIKit.interceptors.response.use(
        function (response) {
            if (requestArray.length != 0) {
                requestArray.forEach(function (x, i) {
                    if (response.config.url == x.url) {
                        requestArray.splice(i, 1);
                    }
                });
            }
            return response;
        },
        function (error) {
            const originalRequest = error.config;
            requestArray.push(originalRequest);
            let reqData = "username=" + number + "&password=" + pin + "&grant_type=password" + "&AppType=2" + "&FcmToken=null";
            // console.log('error ' , error);
            if (error.message === "Request failed with status code 401" || error.statuscode === 401) {
                if (!originalRequest._retry) {
                    originalRequest._retry = true;
                    return axios({
                        method: 'post',
                        url: '/api/login',
                        data: reqData,
                        headers: {
                            "Content-Type": "application/x-www-form-urlencoded",
                            "Cache-Control": "no-cache",
                        }
                    })
                        .then(res => {
                            let response = res.data;
                            console.log('successfull Login', response)
                            if (res.data.StatusCode == 200) {
                                AsyncStorage.setItem('userToken', JSON.stringify(response.access_token));
                                stateFunctions.UserId = response.UserId;
                                stateFunctions.CustomerContactID = response.CustomerContactID;
                                let obj = {
                                    access_token: response.access_token,
                                    token_type: response.token_type,
                                    expires_in: response.expires_in,
                                    UserId: response.UserId,
                                    CustomerContactID: response.CustomerContactID,
                                    Mobile: response.Mobile,
                                    StatusCode: response.StatusCode
                                }
                                AsyncStorage.setItem('logindetail', JSON.stringify(obj));
                                if (requestArray.length != 0) {
                                    requestArray.forEach(x => {
                                        try {
                                            console.log(x, "request Url");
                                            x.headers.Authorization = `Bearer ${response.access_token}`;
                                            x.headers["Content-Type"] = "application/x-www-form-urlencoded";
                                            APIKit.defaults.headers.common["Authorization"] = `Bearer${response.access_token}`;
                                            APIKit(x)
                                        } catch (e) {
                                            console.log(e)
                                        }
                                    });
                                }
                                return APIKit(originalRequest);
                            }
                        })
                        .catch(err => {
                            console.log(err);
                        });
                }
            }
            return Promise.reject(error);
        }
    );
    
    export default APIKit;
    

    Home.js

    gettingToken = async () => {
        let userToken = await AsyncStorage.getItem('userToken');
        userToken = JSON.parse(userToken);
        await setClientToken(userToken);
    }
    

    【讨论】:

      猜你喜欢
      • 2020-01-13
      • 2017-08-22
      • 2020-09-25
      • 2022-11-14
      • 2019-10-04
      • 2020-10-22
      • 2021-01-29
      • 2018-10-31
      • 2017-12-12
      相关资源
      最近更新 更多