首页 > 解决方案 > Vue.js:如何从拦截器代表组件发出请求?

问题描述

在我的 Vue 应用程序中,我有一个响应拦截器:

axios.interceptors.response.use(function (config) {
        return config;
    }, error => {
        if (error.response.status !== 401) {
            return new Promise((resolve, reject) => {
                reject(error);
            });
        }

        if (error.response.status === 401 && error.response.data.message === 'Token Expired') {
            this.store.dispatch('auth/refreshToken').then(aToken => {
                var config = error.config;
                axios.defaults.headers.common['Authorization'] = 'Bearer ' + aToken;

                return new Promise((resolve, reject) => {
                    axios.request(config).then(response => {
                        console.log(response);
                        resolve(response);
                    }).catch((error) => {
                        reject(error);
                    });
                });
            });
        }
    });

...我正在刷新令牌并使用新令牌再次发出最后一个(拦截的)请求。

但问题是,假设我有一个组件,当组件安装时,我在其中向端点Product.vue发出请求。/products我将所有产品存储在products该组件的数据变量中。想象一下用户现在在/dashboard路线中。他去喝了一口咖啡,等他回来的时候,令牌已经过期了。他访问/products路由,响应为 401,因此拦截器拦截该响应,更新令牌,然后尝试使用新令牌发出最后一个失败的请求。

但是这个新请求不是从Product组件执行的。它是从拦截器执行的,我无法访问该Product组件。因此,尽管请求成功,但我的响应数据丢失了,最终用户将在视图中看不到任何内容,因为products变量为空。

有没有办法跟踪哪个组件执行了请求并将其重新安装在拦截器中?我试过$router.push('/products')但 vue 抛出一个异常,指出不允许导航到当前路线。

或者,有什么方法可以处理从 Product 组件中的拦截器返回的承诺?

标签: javascriptvue.jsaxiosinterceptor

解决方案


您需要做的就是保持承诺链运行。目前,您因不返回调度承诺而失去了它。

axios.interceptors.response.use(success => success, error => {
  if (error.response.status === 401 && error.response.data.message === 'Token Expired') {
    // return the new promise
    // not sure what "this" is or why "store" is in it but hey, it's your code
    return this.store.dispatch('auth/refreshToken').then(token => {
      axios.defaults.headers.common.Authorization = `Bearer ${token}`
      return axios.request(error.config)            
    })
  }
  return Promise.reject(error)
})

解释一下,Axios 的响应拦截器将自己插入到请求创建的承诺链中。.then()把它们想象成在您的调用代码接收数据之前在请求的末尾添加一个额外的......

return axios.request({ ... })
  .then(successInterceptor)
  .catch(errorInterceptor)

在您的代码中,您不关心成功的响应,因此您只需返回原始响应

success => success

然后,您的错误拦截器会检查过期的令牌响应并返回一个新的承诺,该承诺以更新令牌开始,然后重复原始请求(请参阅Promise 链接)。

如果响应错误不是针对过期令牌,您只需通过返回拒绝的承诺来维持错误状态。如果你不这样做,被拒绝的承诺就会变成一个成功的承诺,这会让你的调用代码非常混乱。


推荐阅读