Search code examples
vue.jsaxiosvuejs3interceptorvue-router4

Vuejs 3 + Router + Axios (with Interceptor) - Push ignored


my scenario is:

"axios": "^0.26.1",
"vue": "^3.2.25",
"vue-router": "^4.0.14"

I use axios interceptor for two main things:

  • Add a header, and
  • refresh token logic.

before add the code of the axios interceptor, the router.push("/") or router.push({name:"Home"}) worked fine but now they are ignored.

I have read the same issue in other threats, but I have not seen solutions/workaround for continue using router.push outside of the interceptor code.

I hope that you can help me with the next two things:

  • how can I debug in deeper for check the logs inside vue router?
  • if I use axios interceptor, can I continue using router.push command, how?

thanks & regards,

the code:

Axios interceptor

    const setup = (authStore = useAuthStore()) => {
        http.interceptors.request.use(
            (config: AxiosRequestConfig) => {
                const token: string = authStore.getAccessToken
                if (token) {
                    config.headers.Authorization = 'Bearer ' + token
                }
                return config
            },
            (error) => {
                Promise.reject(error)
            }
        ),
        http.interceptors.response.use(
            (res: AxiosResponse) => {
                return res
            },
           async (err:AxiosError) => {
                const originalConfig = err.config
                if (originalConfig.url !== 'auth/login' && err.response) {
                    // access-token expired
                    if (err.response.status === 401 && !originalConfig._retry) {
                        originalConfig._retry = true
                        try {
                           const rs = await http.post('auth/refreshtoken/', { token: authStore.getRefreshToken })
                           const { accessToken } = rs.data
                           authStore.setAccessToken(accessToken)
                           return http(originalConfig)
                        } catch (_error) {
                           return Promise.reject(_error)
                        }
                    }
                }
                return Promise.reject(err)
            }
        )
    }
        
    export default setup

Auth service

    import http from "../utils/http-common"
    
    class AuthService{
        login(data: FormData): Promise<any> {
            return http.post("auth/login/", data, {headers: { "Content-Type": "multipart/form-data" }, timeout: 3000})
        }
    
        me(): Promise<any> {
            return http.get("auth/me/")
        }
    }
    
    export default new AuthService

Vue component

    const login = async () => {
        const formData = new FormData()
        loading.value = true
    
        formData.append("username", credentials.username)
        formData.append("password", credentials.password)
    
        await auth.login(formData)
        .then((res:AxiosResponse) => {
            authStore.setAuthenticated
            authStore.setAccessToken(res.data.access_token)
            authStore.setRefreshToken(res.data.refresh_token)
            router.push({ name: "Home" })
        })
        .catch((e:AxiosError) => {
            alert(e.response?.status + ' ' + e.response?.statusText)
        })
        .finally(() => loading.value = false)

Solution

  • This may maybe work in your case, didn't checked the whole possible issues

    try {
      const response = await auth.login(formData)
      await authStore.setAuthenticated() // may not be needed if not an async Vuex action
      await authStore.setAccessToken(response.data.access_token) // same
      await authStore.setRefreshToken(response.data.refresh_token) // same
      await router.push({ name: "Home" }) // this one is always nice because it's actually async
    } catch (e) {
      alert(e.response?.status + ' ' + e.response?.statusText)
    }
    

    PS: not sure about TS, I don't know how to use it (sorry).