关于javascript:vue/axios中使用vuex的全局http响应错误处理

Global http response error handling in vue/axios with vuex

我正在尝试修复我的 VueJS SPA 中出现不确定状态的行为。应用程序不知道 JWT 已经过期,因此将自己显示为用户仍在登录。例如,这可能在hibernate后发生。

这些用户可以继续向 API 发出任何请求,但最终会得到 401 响应(而且是正确的)。

我想要一个用于 401 响应的全局处理程序。 (这将是:"从 vuex 中清除与用户相关的所有内容,并将页面呈现为好像用户是访客,并弹出登录表单等")否则,我将不得不为每个请求编写一个 401 处理程序。

我可以向 axios 添加响应拦截器,它们工作正常。但是,这些拦截器无法访问 Vuex(或 Vue)。

每当我尝试将 Vuex 或 Vue 导入我的 Axios 时,我都会得到循环依赖(当然)并且一切都中断了。

如果我只是抛出/返回错误,我仍然需要在每个请求上单独处理它。如何从 axios 拦截器中在 this.$store 上分派方法?

Axios 文件包含一个 export default class API,它在 main.js:

中全局添加到 Vue

1
2
3
import api from 'Api/api'
// ...
Vue.prototype.$http = api

我认为必须有一种方法可以从 $http 访问 Vue,因为它是一个全局实例方法。但我好像弄错了?

代码

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
// ...
import api from 'Api/api'
// ...
Vue.prototype.$http = api

new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App },
  vuetify: new Vuetify(opts),
});

api.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Client from './ApiClient'

const apiClient = new Client({ basePath: process.env.VUE_APP_API_URL })

const api = {
  get(url) {
    return apiClient._get(`${basePath}/${url}`)
  },
  post(url, data) {
    return apiClient._post(`${basePath}/${url}`, data)
  },
  // ...
}
export default api

ApiClient.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const axios = require('axios')

const errorHandler = (error) => {
  if (error.response.status === 401) {
    store.dispatch('user/logout') // here is the problem
  }
  return Promise.reject({ ...error })
}


export default class API {
  constructor(options) {
    this.options = Object.assign({ basePath: '' }, options)
    this.axios = axios.create({ timeout: 60000 })
    this.axios.interceptors.response.use(
      response => response,
      error => errorHandler(error)
    )
  }
  // ...
}

ApiClient.js 中导入 store 会导致依赖循环:我假设是因为我正在其中导入 Vue?

store.js

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import Vuex from 'vuex'
import PersistedState from 'vuex-persistedstate'
import CreateMutationsSharer from 'vuex-shared-mutations';
import SecureLS from 'secure-ls';
// import modules

Vue.use(Vuex);
const ls = new SecureLS({ encodingType: 'aes' });

export default new Vuex.Store({
  // options
})


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
conf
import Axios from 'axios'
import IdentityProxy from './IdentityProxy.js'
import UsuariosProxi from './UsuariosProxi'
import ZonasProxi from './ZonasProxi'

//axios
Axios.defaults.headers.common.Accept='application/json'
//Axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';

Axios.interceptors.request.use(
    config => {
        let token = localStorage.getItem('access_token');

        if(token){
            config.headers= {
                'x-access-token': `${token}`
            }
        }
        return config;
    },
    error => Promise.reject(error)
);
Axios.interceptors.response.use(
    response => response,
    error => {
      if (error.response.status===403||error.response.status===401) {
        localStorage.removeItem('access_token');
        window.location.reload(true);
      }
   
      return Promise.reject(error);
    }
  );
let url=null

if(localStorage.getItem("config")!==null){
    let config = JSON.parse(localStorage.getItem("config"))
    url = config
}

console.log(url)
export default{
    identityProxy: new IdentityProxy(Axios, url),
    _usuarioProxi: new UsuariosProxi(Axios, url),
    _zonasProxi: new ZonasProxi(Axios, url),
}
//
export default class IdentityProxy{

    constructor(axios,url){
    this.axios = axios;
    this.url =url;
    }

    register(params){
        return this.axios.post(this.url+'/identity/register',params)
    }

    login(params){
       
        return this.axios.post(this.url+'/auth/signin',params)
    }
}
//
export default class UsuariosProxi{
    constructor(axios,url){
    this.axios = axios;
    this.url =url;
    }

    /* getAll(){
        return this.axios.get(this.url+'/users')
    } */

    getAll(page, take) {
        return this.axios.get(this.url + `/users?page=${page}&take=${take}`);
    }
    create(params) {
        return this.axios.post(this.url + '/auth/signup', params);
    }

    get(id) {
        return this.axios.get(this.url + `/users/${id}`);
    }
    update(id, params) {
        return this.axios.put(this.url + `/users/${id}`, params);
    }

    remove(id) {
        return this.axios.delete(this.url + `/users/${id}`);
    }
    //-----APARTE SOLO TRAE LISTA DE ROLES--------
    getRoles() {
        return this.axios.get(this.url + '/users/newrol');
    }
}
//st
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const state = {
    user:null
}
export default new Vuex.Store({
    state
});

基于这些线程,我能够管理满足我需求的解决方案:

main.js

1
2
import api, {apiConfig} from 'Api/api'
apiConfig({ store: $store });

ApiClient.js

1
2
3
4
5
6
7
8
let configs = {
  store: undefined,
};
const apiConfig = ({ store }) => {
  configs = { ...configs, store };
};
export default api;
export { apiConfig };

这样api.js文件就需要一个配置,以后可以扩展。


main.js:

1
2
3
4
5
6
7
8
import store from './store';

const Instance = new Vue({
  store,
  ...
})

export const { $store } = Instance;

现在您可以import { $store } from '@/main.js'随心所欲。它将是您在应用程序中安装的同一个实例,而不是 new Vuex.Store({}) (这是 ./store 导出的内容,每次您将其导入其他地方时)。

您可以以相同的方式导出您可能想在服务、测试、帮助程序等中使用的任何其他内容...即:

1
export const { $store, $http, $bus, $t } = Instance;


直接将您的商店导入 ApiClient.js 怎么样?类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const axios = require('axios')
import store from 'path/to/store'

const errorHandler = (error) => {
if (error.response.status === 401) {
  store.dispatch('user/logout') // now store should be accessible
}
  return Promise.reject({ ...error })
}


export default class API {
  constructor(options) {
    this.options = Object.assign({ basePath: '' }, options)
    this.axios = axios.create({ timeout: 60000 })
    this.axios.interceptors.response.use(
      response => response,
      error => errorHandler(error)
    )
  }
  // ...
}