问题
I'm trying to fix a behavior in my vue SPA which is basically a limbo state where the app doesn't know the JWT is already expired and therefore presents itself as if the user was still logged in. (this happens after hibernation for example)
These users can keep on making any request to the API, but end up with a 401
response of course
Now I would like to have a global handler for 401
responses. (which would be: "clear everything user-related from vuex and present the page as if the user was a guest, with login form popup etc"), otherwise i would have to write a 401 handler for EVERY request
the problem being: I can add response interceptors to axios, and they work fine. BUT they don't have access to vuex (or Vue).
Whenever i try to import vuex or Vue into my axios, i get circular dependencies (of course) and everything breaks.
If i just throw/return the error, i will STILL have to handle it separately on every request.
How can i dispatch methods on this.$store
from within an axios interceptor?
(more info:)
the axios file contains an export default class API
which is added to Vue globally in main.js via
import api from 'Api/api'
// ...
Vue.prototype.$http = api
so i thought there has to be a way to access Vue
from $http
, since, it's a global instance method... am I wrong?
how can I solve it?
Code (as far as possible and relevant):
main.js
// ...
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
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
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)
)
}
// ...
}
update
importing store in ApiClient.js results in a dependency cycle. i don't really know why, but i assume because i'm importing Vue in it?
store.js
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
})
If you need any more info, just ask :)
Thanks for any help
回答1:
main.js:
import store from './store';
const Instance = new Vue({
store,
...
})
export const { $store } = Instance;
Now you can import { $store } from '@/main.js'
anywhere you want. And it's going to be the same instance you have mounted in your app, not a new Vuex.Store({})
(which is what ./store
exports, each time you import it somewhere else).
You can export the same way anything else you might want to use in services, tests, helpers, etc... I.e:
export const { $store, $http, $bus, $t } = Instance;
回答2:
What about direct import your store to ApiClient.js? Something like
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)
)
}
// ...
}
来源:https://stackoverflow.com/questions/61613545/global-http-response-error-handling-in-vue-axios-with-vuex