Javascript 取消axios时出现问题。然后使用拦截器和取消令牌执行逻辑

Javascript 取消axios时出现问题。然后使用拦截器和取消令牌执行逻辑,javascript,reactjs,redux,axios,redux-persist,Javascript,Reactjs,Redux,Axios,Redux Persist,我为我的react应用程序设置了axios响应拦截器。它可以很好地捕捉大多数错误,但我遇到的一个问题是,如果响应是401,即用户未经授权,拦截器会将用户发送回登录页面。现在这可以工作了,但是内部的逻辑仍然在运行。这会导致一个类型错误,如逻辑中所示。然后,我正在使用响应数据设置一个状态。下面是我当前尝试实现的axios取消令牌,但该令牌不起作用。请参阅下面的代码。我错过了什么?在不必向每个axios请求添加If/Else逻辑以检查“数据”是否存在或响应是否为401200…,实现这一点的最佳方法是什

我为我的react应用程序设置了axios响应拦截器。它可以很好地捕捉大多数错误,但我遇到的一个问题是,如果响应是401,即用户未经授权,拦截器会将用户发送回登录页面。现在这可以工作了,但是内部的逻辑仍然在运行。这会导致一个类型错误,如逻辑中所示。然后,我正在使用响应数据设置一个状态。下面是我当前尝试实现的axios取消令牌,但该令牌不起作用。请参阅下面的代码。我错过了什么?在不必向每个axios请求添加If/Else逻辑以检查“数据”是否存在或响应是否为401200…,实现这一点的最佳方法是什么

AxiosInterceptor.js

UserPage.js

我得到的错误:

Unhandled Rejection (TypeError): Cannot read property 'data' of undefined
进度更新: 我在后端添加了一些逻辑,如果登录成功

  • 我将JWT令牌的过期时间传递回我的前端

  • 然后把过期时间推到我的redux商店

  • 在每个请求中,在我下面的“axiositerceptor.js”文件中,在返回配置之前,我验证在redux中设置的exp值

  • 现在,这在初次登录时可以正常工作,但一旦令牌过期,您收到来自“Swal.fire”的弹出窗口并单击“return”,它将执行两项操作:

  • 调用注销操作并将所有值返回到初始状态。(这很好。我使用redux devtools扩展进行了验证)
  • 现在我可以重新登录了。一切开始加载良好,但我得到'Swal.fire'对话框返回到登录页面。将user.exp和date.now记录到控制台时,我看到一些奇怪的行为(请参阅注释):
  • 这怎么可能?我用redux开发工具验证了一旦我 返回到登录页面,它确实是空的。>redux存储中的值似乎正在回滚到旧值?我使用的是chrome版本 74.0.3729.131(正式构建)(64位)。我尝试过隐姓埋名模式和清除缓存和cookies

    新建AxiosInterceptor.js…

    export default withRouter({
        useSetupInterceptors: (history) => {
        let user = useSelector(state => state.user) 
            axios.interceptors.request.use(config => {
             const { onLogo } = useLogout(history);
                    console.log("Current Status = ", user.exp, Date.now() > user.exp)
                    if (Date.now() > user.exp) {
                        Swal.fire({
                            title: '401 - Auth Failed',
                            text: '',
                            icon: 'warning',
                            showCancelButton: false,
                            confirmButtonText: 'Return',
                        }).then((result) => {
                            onLogo();
                        })
                        return {
                            ...config,
                            cancelToken: new CancelToken((cancel) => cancel('Cancel')) // Add cancel token to config to cancel request if redux-store expire value is exceeded
                          };
                    } else {
                        return config;
                    }
                  }, error => { console.log(error)});
    
            axios.interceptors.response.use(response => {
                return response;
            }, error => {
                try {
                if (axios.isCancel(error)) { // check if canceled
                        return new Promise(() => {}); // return new promise to stop axios from proceeding to the .then
                    }
                    if (error.response.status === 401) {
                        history.push("/login");
                        Swal.fire({
                            title: '401 - Auth Failed',
                            text: '',
                            icon: 'warning',
                            showCancelButton: false,
                            confirmButtonText: 'Close',
                        })
                        throw new axios.Cancel('Operation canceled');
                    }
                    return Promise.reject(error);
                } catch (error) {
                    console.log(error)
                }
            });
        },
    });
    
    function useLogo(history) {
        const dispatch = useDispatch()
        return {
            onLogo() {
                dispatch(allActs.userActs.logOut())
                history.push("/login");
            },
        }
    }
    

    我在react redux中找到了钩子“useSelector”的问题。这似乎是在缓存数据已经返回正确数据之后,如何返回缓存数据。我当时使用的是7.2版,但我也在7.1版上确认了这一点。我没有测试过任何其他版本。我通过在下面的
    getExpire()
    函数中从redux persist Storage(localStorage)提取数据来解决这个问题。这不是最优雅的解决方案,但我的应用程序现在可以正常工作了

    export default withRouter({
        useSetupInterceptors: (history) => {
            const { onLogout } = useLogout(history);
            const CancelToken = axios.CancelToken;
            const { onExp } = useExp();
    
            axios.interceptors.request.use((config) => {
                const testexp = onExp();
                if (testexp) {
                    Swal.fire({
                        title: '401 - Authorization Failed',
                        text: '',
                        icon: 'warning',
                        showCancelButton: false,
                        confirmButtonText: 'Return',
                    }).then((result) => {
                        onLogout();
    
                    })
                    return {
                        ...config,
                        cancelToken: new CancelToken((cancel) => cancel('Cancel repeated request'))
                    };
                } else {
                    return config;
                }
            }, error => { console.log(error) });
    
            axios.interceptors.response.use(response => {
                return response;
            }, error => {
                try {
                    if (axios.isCancel(error)) {
                        return new Promise(() => { });
                    }
                    return Promise.reject(error);
                } catch (error) {
                    console.log(error)
                }
            });
        },
    });
    
    function getExpire () {
        var localStore = localStorage.getItem("persist:root")
        if (localStore) {
           let store = JSON.parse(localStore)
           return JSON.parse(store.exp)
        } 
        return 0
    
    }
    
    function useExp() {
       // const currentExp = useSelector(state => state.exp)
        return {
            onExp() {
                if (Date.now() > getExpire().exp) {
                    return true
                } else { return false }
            },
        }
    }
    
    function useLogout(history) {
        const dispatch = useDispatch()
        return {
            onLogout() {
                dispatch(allActions.expAction.setLogout())
                history.push("/login");
            },
        }
    }
    

    检查什么是响应
    然后(res=>{console.log(res)})
    感谢您的快速响应!我知道我可以检查响应并使用If/Else逻辑来决定在401事件中应该做什么,但我想看看是否有一种方法可以在拦截器/全局级别这样做,而不必将其添加到每个页面上的每个axios请求中。不幸的是,我查看了那篇文章,似乎我的问题已经从那里转移了。你知道吗检查axios中间件?您还可以创建自己的中间件来全局处理错误。
    // from redux-logger
    action SET_EXP @ 20:05:42.721
    redux-logger.js:1  prev state {user: {…}, _persist: {…}}
    redux-logger.js:1  action     {type: "SET_EXP", payload: 1585267561036}
    
    USEREXP 1585267561036 // this is the new EXP time set in redux, received from back end on login
    AxiosInterceptors.js:17 Current Status =  1585267561036 false // first two axios calls on main page validate and indicate not expired
    AxiosInterceptors.js:17 Current Status =  1585267561036 false
    AxiosInterceptors.js:17 Current Status =  1585267495132 true // this is the value of the previos exp value that was set
    AxiosInterceptors.js:17 Current Status =  1585267495132 true
    AxiosInterceptors.js:17 Current Status =  1585267352424 true // this is the value that was set two login times ago
    AxiosInterceptors.js:17 Current Status =  1585267352424 true
    
    export default withRouter({
        useSetupInterceptors: (history) => {
        let user = useSelector(state => state.user) 
            axios.interceptors.request.use(config => {
             const { onLogo } = useLogout(history);
                    console.log("Current Status = ", user.exp, Date.now() > user.exp)
                    if (Date.now() > user.exp) {
                        Swal.fire({
                            title: '401 - Auth Failed',
                            text: '',
                            icon: 'warning',
                            showCancelButton: false,
                            confirmButtonText: 'Return',
                        }).then((result) => {
                            onLogo();
                        })
                        return {
                            ...config,
                            cancelToken: new CancelToken((cancel) => cancel('Cancel')) // Add cancel token to config to cancel request if redux-store expire value is exceeded
                          };
                    } else {
                        return config;
                    }
                  }, error => { console.log(error)});
    
            axios.interceptors.response.use(response => {
                return response;
            }, error => {
                try {
                if (axios.isCancel(error)) { // check if canceled
                        return new Promise(() => {}); // return new promise to stop axios from proceeding to the .then
                    }
                    if (error.response.status === 401) {
                        history.push("/login");
                        Swal.fire({
                            title: '401 - Auth Failed',
                            text: '',
                            icon: 'warning',
                            showCancelButton: false,
                            confirmButtonText: 'Close',
                        })
                        throw new axios.Cancel('Operation canceled');
                    }
                    return Promise.reject(error);
                } catch (error) {
                    console.log(error)
                }
            });
        },
    });
    
    function useLogo(history) {
        const dispatch = useDispatch()
        return {
            onLogo() {
                dispatch(allActs.userActs.logOut())
                history.push("/login");
            },
        }
    }
    
    export default withRouter({
        useSetupInterceptors: (history) => {
            const { onLogout } = useLogout(history);
            const CancelToken = axios.CancelToken;
            const { onExp } = useExp();
    
            axios.interceptors.request.use((config) => {
                const testexp = onExp();
                if (testexp) {
                    Swal.fire({
                        title: '401 - Authorization Failed',
                        text: '',
                        icon: 'warning',
                        showCancelButton: false,
                        confirmButtonText: 'Return',
                    }).then((result) => {
                        onLogout();
    
                    })
                    return {
                        ...config,
                        cancelToken: new CancelToken((cancel) => cancel('Cancel repeated request'))
                    };
                } else {
                    return config;
                }
            }, error => { console.log(error) });
    
            axios.interceptors.response.use(response => {
                return response;
            }, error => {
                try {
                    if (axios.isCancel(error)) {
                        return new Promise(() => { });
                    }
                    return Promise.reject(error);
                } catch (error) {
                    console.log(error)
                }
            });
        },
    });
    
    function getExpire () {
        var localStore = localStorage.getItem("persist:root")
        if (localStore) {
           let store = JSON.parse(localStore)
           return JSON.parse(store.exp)
        } 
        return 0
    
    }
    
    function useExp() {
       // const currentExp = useSelector(state => state.exp)
        return {
            onExp() {
                if (Date.now() > getExpire().exp) {
                    return true
                } else { return false }
            },
        }
    }
    
    function useLogout(history) {
        const dispatch = useDispatch()
        return {
            onLogout() {
                dispatch(allActions.expAction.setLogout())
                history.push("/login");
            },
        }
    }