Проблема с отменой топора ios .then logi c с перехватчиками и отмена токена - PullRequest
0 голосов
/ 26 марта 2020

Я установил перехватчик ответа ios для моего приложения реакции. Он отлично работает для перехвата большинства ошибок, но у меня возникли проблемы: если ответ 401 или пользователь не авторизован, перехватчик отправляет пользователя обратно на страницу входа. Теперь это работает, но логика c внутри .then из исходного запроса все еще работает. Это вызывает ошибку типа, как в .then logi c Я устанавливаю состояние с данными ответа. Вот моя текущая попытка реализовать токен отмены топора ios, который не работает. Смотрите код ниже. Что мне здесь не хватает? Каков наилучший способ добиться этого без необходимости добавлять If / Else logi c к каждому запросу axe ios, чтобы проверить, есть ли «данные» или ответ 401, 200 ...?

AxiosInterceptor. js ...

export default withRouter({
    useSetupInterceptors: (history) => {
        axios.interceptors.response.use(response => {
            return response;
        }, error => {
            try {
                if (error.response.status === 401) {
                    history.push("/login");
                    Swal.fire({
                        title: '401 - Authorization Failed',
                        text: '',
                        icon: 'warning',
                        showCancelButton: false,
                        confirmButtonText: 'Close',
                    })
                    throw new axios.Cancel('Operation canceled');
                }
                return Promise.reject(error);
            } catch (error) {
                console.log(error)
            }
        });
    },
});

UserPage. js ...

function userPage() {
  var [pageData, setPageData] = useState('');
  var classes = useStyles();


  useEffect(() => {
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
    const loadData = () => {
      try {
      axios.post('/api/getUserData', { cancelToken: source.token })
        .catch(function (error) {
          source.cancel();
        })
        .then(res => {
            const data = res.data;
            setPageData(data);
        })
      } catch (error) {
        if (axios.isCancel(error)) {
          console.log('Op Cancel')
        } else {
          throw error;
        }
      }
    };
      loadData();
    return () => {
      source.cancel();
    };
  }, []);

  return ( 
     ... 
  );
}
...

Ошибка, которую я получаю:

Unhandled Rejection (TypeError): Cannot read property 'data' of undefined

ОБНОВЛЕНИЕ ПРОГРЕССА:

Я добавил логи c в свой бэкэнд, что если вход в систему успешен,

  1. я передаю время истечения токена JWT обратно в мой интерфейс.

  2. Затем пу sh этой эпохи истечения срока в мой магазин редуксов.

  3. По каждому запросу в моем 'AxiosInterceptor. js' файл ниже, прежде чем вернуть конфигурацию обратно, я проверяю значение exp, установленное в redux.

Теперь это работает нормально при первоначальном входе в систему, но как только токен истек, и вы получаете всплывающее окно от «Swal.fire» и нажатие «return» делает две вещи:

  1. вызывает действие logOut и возвращает все значения в исходное состояние. (Это отлично работает. Я проверил с помощью redux-devtools-extension)
  2. Теперь я могу войти в систему. Все начинает загружаться нормально, но затем я получаю диалог «Swal.fire», чтобы вернуться обратно на страницу входа. При входе user.exp и date.now в консоль я вижу странное поведение (см. Комментарии):
// 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

Как это возможно? Я проверил с помощью redux-devtools, что, как только я возвращаюсь на страницу входа, она действительно пуста. Похоже, что значение в> redux-store возвращается к старым значениям? Я использую chrome Версия 74.0.3729.131 (Официальная сборка) (64-разрядная версия). Я пытался с режимом инкогнито и очистки кеша и куки.

Новый 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");
        },
    }
}

1 Ответ

0 голосов
/ 30 марта 2020

Я отследил проблему до хука "useSelector" в реакции-избыточности. Похоже, это как-то возвращает кешированные данные, после того как уже вернулись правильные данные. Я использую версию 7.2 в свое время, но я подтвердил это также на v7.1. Я не проверял ни на каких других версиях. Я решил эту проблему, извлекая данные из хранилища redux-persist (localStorage) в функции getExpire() ниже. Не самое элегантное решение, но мое приложение теперь работает так, как должно быть.

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");
        },
    }
}
...