I'm trying to fix a behavior in my VueJS SPA wherein a limbo state arises. The app doesn't know the JWT has already expired and therefore presents itself as if the user is still logged in. This can happen after hibernation, for example.
These users can keep on making any request to the API, but end up with a 401
response (and correctly so).
I'd like to have a global handler for 401
responses. (This 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.
I can add response interceptors to axios, and they work fine. These interceptors don't have access to Vuex (or Vue), though.
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 still have to handle it separately on every request. How can I dispatch methods on this.$store
from within an axios interceptor?
The Axios file contains an export default class API
that is added to Vue globally in main.js
:
import api from 'Api/api'
// ...
Vue.prototype.$http = api
I had thought there has to be a way to access Vue
from $http
, since it's a global instance method. But I appear to be mistaken?
Code
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)
)
}
// ...
}
Importing store in ApiClient.js
results in a dependency cycle: 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
})