Как перенаправить с перехватчика axios с помощью Router V4?

Я хочу сделать переадресацию в перехватчиках axios при получении ошибки 403. Но как я могу получить доступ к истории вне компонентов React?

В Навигация по программному обеспечению в React-Router v4, это в контексте компонента React, но здесь я пытаюсь в контексте axios

axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    if(error.response.status === 403) { console.log("Redirection needed !"); }

    // Trow errr again (may be need for some other catch)
    return Promise.reject(error);
});

Ответ 1

Я решил, что, обратившись к моему магазину Redux из-за дерева Component и отправив ему одно и то же действие с кнопки выхода, поскольку мои перехватчики создаются в отдельном файле и загружаются до загрузки любого компонента.

Итак, в основном, я сделал следующее:

В index.js файле:

//....lots of imports ommited for brevity
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
import reducers from './reducers';
import { UNAUTH_USER } from './actions/types'; //this is just a constants file for action types.

const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = createStoreWithMiddleware(reducers);

//Here is the guy where I set up the interceptors!
NetworkService.setupInterceptors(store);

//lots of code ommited again...
//Please pay attention to the "RequireAuth" below, we'll talk about it later

ReactDOM.render(
  <Provider store={store}>
      <BrowserRouter>
          <div>
              <Header />
              <main className="plan-container">
                  <Switch>
                      <Route exact path="/" component={Landing} />
                      <Route exact path="/login" component={Login} />
                      <Route exact path="/signup" component={Signup} />
                      <Route exact path="/calendar" component={RequireAuth(Calendar)} />
                      <Route exact path="/profile" component={RequireAuth(Profile)} />
                  </Switch>
              </main>
          </div>
      </BrowserRouter>
  </Provider>
  , document.querySelector('.main-container'));

И в файле network-service.js:

import axios        from 'axios';
import { UNAUTH_USER } from '../actions/types';

export default {
  setupInterceptors: (store) => {

    // Add a response interceptor
    axios.interceptors.response.use(function (response) {
        return response;
    }, function (error) {
        //catches if the session ended!
        if ( error.response.data.token.KEY == 'ERR_EXPIRED_TOKEN') {
            console.log("EXPIRED TOKEN!");
            localStorage.clear();
            store.dispatch({ type: UNAUTH_USER });
        }
        return Promise.reject(error);
    });

  }
};

И последнее, но не менее важное: у меня есть HOC (компонент более высокого порядка), который я обертываю защищенными компонентами, где я делаю фактическую переадресацию, когда сеанс отсутствует. Таким образом, когда я запускаю тип действия UNAUTH_USER, он устанавливает мое свойство isLogged в моем редукторе session на false, и поэтому этот компонент получает уведомление и делает перенаправление для меня в любое время.

Файл для компонента require-auth.js:

import React, { Component } from 'react';
import { connect } from 'react-redux';

export default function(ComposedComponent) {

    class RequireAuth extends Component {

        componentWillMount() {
            if(!this.props.session.isLogged) {
                this.props.history.push('/login');
            }
        };

        componentWillUpdate(nextProps) {
            if(!nextProps.session.isLogged) {
                this.props.history.push('/login');
            }
        };

        render() {
            return <ComposedComponent {...this.props} />
        }
    }

    function mapStateToProps(state) {
        return { session: state.session };
    }

    return connect(mapStateToProps)(RequireAuth);
}

Надеюсь, что это поможет!

Ответ 2

Я решил эту задачу, создав историю браузера из пакета history (https://github.com/ReactTraining/history) и передав его в функцию-перехватчик, а затем вызвав из нее метод .push().

Основной код файла (часть его):

// app.js
import createHistory from 'history/createBrowserHistory';
import httpService from './api_client/interceptors';

...

const history = createHistory();
httpService.setupInterceptors(store, history);

Конфигурация перехватчика:

import axios from 'axios';

export default {
  setupInterceptors: (store, history) => {

      axios.interceptors.response.use(response => {
        return response;
      }, error => {

      if (error.response.status === 401) {
        store.dispatch(logoutUser());
      }

      if (error.response.status === 404) {
         history.push('/not-found');
      }

      return Promise.reject(error);
    });
  },
};

Кроме того, вы должны использовать Router from react-router (https://github.com/ReactTraining/react-router) и передавать тот же объект history параметр history.

// app.js
...
ReactDOM.render(
  <Provider store={store}>
     <Router history={history}>
        ...
     </Router>
  </Provider>
, document.getElementById('#root'))

Надеюсь это поможет!

Ответ 3

Лучшее решение, которое я нашел, - определить axios.interceptors внутри моих основных компонентов React и использовать that для обработки ошибок: (И с withRouter от Router V4)

import {withRouter} from 'react-router-dom';

class Homepage extends Component {
  static propTypes = {
    history: PropTypes.object.isRequired
  }

  constructor(props){
    super(props);

    let that = this;
    axios.interceptors.response.use(function (response) {
        // Do something with response data
        return response;
      }, function (error) {
        // Do something with response error
        if(error.response.status === 403) { that.handle403() }

        // Trow errr again (may be need for some other catch)
        return Promise.reject(error);
    });

  }

  handle403(){
    this.props.history.push('/login');
  }

Ответ 4

Попробовали ли вы сделать <Redirect>? Это стандартный способ перенаправления с v4. Я не уверен, где живет ваш код, но пока вы импортируете React, он должен работать.

axios.interceptors.response.use(function (response) {
    return response;
  }, function (error) {
    // Do something with response error
    if(error.response.status === 403) { 
      return <Redirect to='/somePage' />
    }

    // Trow errr again (may be need for some other catch)
    return Promise.reject(error);
});

Здесь документация для <Redirect>

Ответ 5

Кажется, это работает для меня

 function (error) {
            var accessDenied = error.toString().indexOf("401");
            if (accessDenied !== -1) {
              console.log('ACCESS DENIED')
              return window.location.href = '/accessdenied'
            }
          });

Ответ 6

Я использую response-router-dom и имеет реквизит "history", который можно использовать при переходе на новый маршрут

 history.push('/newRoute')

Ответ 7

Здесь я делаю это так:

index.js:

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import NetworkService from './_helpers/network-service';
import { store, defaultApi } from './_helpers';
import { App } from './App';

defaultApi();

NetworkService.setupInterceptors(store);

render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
);

/_ хелперы/сети service.js:

import axios from 'axios';
import { history } from '../_helpers';

export default {
    setupInterceptors: (store) => {
        axios.interceptors.response.use(function (response) {
            return response;
        }, function (error) {
            if (error.response.status === 401) {
                history.push('/login');
            }
            return Promise.reject(error);
        });

    }
};

/_ хелперы /history.js:

import { createBrowserHistory } from 'history';

export const history = createBrowserHistory();

и на основном маршруте я также называю историю:

...
<Router history={history}>
...

Ответ 8

Принятый ответ не решает мою проблему. Проведя время в axios и билетах вокруг перехватчика, который не срабатывает, я обнаружил, что axios не поддерживает глобальное оформление перехватчика, как описано выше. для будущих читателей, пожалуйста, имейте в виду, что axios отметил этот global interceptor как функцию. так что, возможно, мы получим это в будущем. для ссылки: https://github.com/axios/axios/issues/993.

У меня есть один экземпляр axios для всех вызовов API, поэтому я решил определить в нем перехватчик.

Ответ 9

import { createHashHistory } from 'history' // or createBrowserHistory
const history = createHashHistory()

if (response.status === 403) {
  history.push('/login')
}

ReactTraining/история