开发者问题收集

未捕获(在承诺中)TypeError:在axios中刷新令牌时无法读取未定义的属性(读取'then')

2023-04-22
459

我想在 axios "axios": "^1.3.4", 中后台刷新 token,当服务端返回的 access token 过期后,客户端将请求存储在队列中,并在新的 token 刷新后恢复请求。当我在 react 中使用 promise 时,如下所示:

instance.interceptors.response.use((response:AxiosResponse<any, any>) => {
  const originalRequest:InternalAxiosRequestConfig<any> = response.config;
  if(isRefreshing){
    addRequestToQueue(originalRequest);
  }
  if (!isRefreshing) {
    if(response.data.resultCode === ResponseCode.ACCESS_TOKEN_EXPIRED){
      addRequestToQueue(originalRequest);
      isRefreshing = true;
      // refresh the access token
      ResponseHandler.handleWebCommonFailure(response.data)
      .then((data:any) => {
        isRefreshing = false;
        pendingRequestsQueue.forEach((request) => {
          const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
          request.resolve(accessToken)
          .then((resp:any)=>{
            // get the action of original request
            const action = request.action;
            const data = resp.data.result;
            // change the state to make it render the UI
            store.dispatch(action(data));
          });
        });
        pendingRequestsQueue = [];
      });
    }
  }
  return response;
},
  (error: any) => { return Promise.reject(error) }
)

此行 request.resolve(accessToken) 显示错误 caught (in promise) TypeError: Cannot read properties of undefined (reading 'then') ,这是 addRequestToQueue 函数定义:

function addRequestToQueue(originalRequest: any){
  return new Promise((resolve, reject) => {
    pendingRequestsQueue.push({ resolve, reject });
  })
    .then((data:any) => {
      originalRequest.headers['x-access-token'] = data.accessToken;
      originalRequest.headers['x-request-id'] = uuid();
      return instance(originalRequest);
    })
    .catch((err) => {
      return Promise.reject(err);
    });
}

我应该怎么做才能解决这个问题?这是我的 axios 客户端的完整代码:

import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { v4 as uuid } from 'uuid';
import store from '../store/store';
import { ResponseCode, ResponseHandler, WheelGlobal } from 'js-wheel';

let isRefreshing = false
let pendingRequestsQueue: Array<any> = [];

const instance = axios.create({
  timeout: 60000
})

instance.defaults.headers.post['Content-Type'] = 'application/json'

instance.interceptors.request.use((request) => {
  const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
  accessToken && (request.headers['x-access-token'] = accessToken);
  request.headers['x-request-id'] = uuid();
  return request
},
  (error: any) => {
    return Promise.reject(error)
  }
)

function addRequestToQueue(originalRequest: any): Promise<any> {
  return new Promise((resolve, reject) => {
    pendingRequestsQueue.push({ resolve, reject });
  })
    .then((data: any) => {
      originalRequest.headers['x-access-token'] = data.accessToken;
      originalRequest.headers['x-request-id'] = uuid();
      return instance(originalRequest);
    })
    .catch((err) => {
      return Promise.reject(err);
    });
}

instance.interceptors.response.use((response: AxiosResponse<any, any>) => {
  const originalRequest: InternalAxiosRequestConfig<any> = response.config;
  if (isRefreshing) {
    addRequestToQueue(originalRequest);
  }
  if (!isRefreshing) {
    if (response.data.resultCode === ResponseCode.ACCESS_TOKEN_EXPIRED) {
      addRequestToQueue(originalRequest);
      isRefreshing = true;
      // refresh the access token
      ResponseHandler.handleWebCommonFailure(response.data)
        .then((data: any) => {
          isRefreshing = false;
          pendingRequestsQueue.forEach((request) => {
            const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
            const promise = request.resolve(accessToken);
            if (promise) {
              promise.then((resp: any) => {
                // get the action of original request
                const action = request.action;
                const data = resp.data.result;
                // change the state to make it render the UI
                store.dispatch(action(data));
              });
            }
          });
          pendingRequestsQueue = [];
        });
    }
  }
  return response;
},
  (error: any) => { return Promise.reject(error) }
)

export function requestWithAction(config: any, action: (arg0: any) => any) {
  return instance(config).then(
    (response: { data: { result: any; }; }) => {
      const data = response.data.result;
      store.dispatch(action(data));
      return response.data;
    }
  ).catch(
    (error: any) => {
      console.error(error);
    }
  );
}
2个回答

问题是 resolve 不返回承诺。它返回 undefined

退一步说,您不需要像 addRequestToQueue 这样的函数来创建新的承诺并将其 resolve 函数推送到队列中。当您已经有要使用的诺言 ( instance() ) 时创建 new Promise 是一种反模式。

相反,将请求放入队列并在主函数中调用 axios ( instance ),您可以在其上链接 then 调用。像这样:

instance.interceptors.response.use((response:AxiosResponse<any, any>) => {
  const originalRequest:InternalAxiosRequestConfig<any> = response.config;
  if(isRefreshing){
    pendingRequestsQueue.push(originalRequest); // <--
  }
  if (!isRefreshing) {
    if(response.data.resultCode === ResponseCode.ACCESS_TOKEN_EXPIRED){
      pendingRequestsQueue.push(originalRequest); // <--
      isRefreshing = true;
      // refresh the access token
      ResponseHandler.handleWebCommonFailure(response.data)
      .then((data:any) => {
        isRefreshing = false;
        pendingRequestsQueue.forEach((request) => {
          const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
          // Moved code from `addRequestsToQueue` here:
          request.headers['x-access-token'] = accessToken;
          request.headers['x-request-id'] = uuid();
          instance(request) // This returns a promise (contrary to `resolve`)
          .then((resp:any)=>{
            // get the action of original request
            const action = request.action;
            const data = resp.data.result;
            // change the state to make it render the UI
            store.dispatch(action(data));
          });
        });
        pendingRequestsQueue = [];
      });
    }
  }
  return response;
},
  (error: any) => { return Promise.reject(error) }
)
trincot
2023-04-22

您可以在句点“.”前使用“?”,并在代码中使用 typeof "something" !== "undefined" ,您可以执行 request?.resolve(accessToken)

sina soleymanzadeh
2023-04-22