web-dev-qa-db-fra.com

Comment rediriger depuis axios interceptor avec react Router V4?

Je souhaite effectuer une redirection dans les intercepteurs axios lors de la réception d'une erreur 403. Mais comment puis-je accéder à l'historique en dehors des composants React?

Dans Navigation programmatique dans React-Router v4 , c'est dans le contexte d'un composant React, mais ici j'essaie dans un contexte 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);
});
10
hilderic sb

J'ai résolu ce problème en accédant à mon magasin Redux en dehors de l'arborescence des composants et en lui envoyant la même action depuis le bouton de déconnexion, car mes intercepteurs sont créés dans un fichier séparé et chargés avant le chargement de tout composant. 

Donc, en gros, j'ai fait ce qui suit:

Au fichier 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'));

Et au fichier 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);
    });

  }
};

Dernier point, mais non des moindres, j'ai un HOC (composant d'ordre supérieur) que j'emballe mes composants protégés où je fais la redirection réelle lorsque la session est terminée. Ainsi, lorsque je déclenche le type d'action UNAUTH_USER, il définit ma propriété isLogged sur mon réducteur session sur false et, par conséquent, ce composant est averti et effectue la redirection pour moi à tout moment.

Le fichier pour le composant 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);
}

J'espère que cela pourra aider!

18
DccBr

J'ai résolu cette tâche en créant l'historique du navigateur à partir du package history ( https://github.com/ReactTraining/history ) et en le passant dans la fonction d'intercepteur, puis en appelant la méthode .Push()

Le code de fichier principal (une partie):

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

...

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

Configuration d'intercepteur:

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

En outre, vous devez utiliser Router à partir de react-router ( https://github.com/ReactTraining/react-router ) et transmettre le même objet d’historique que history param.

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

J'espère que cela t'aides!

11
Denys Kotsur

La meilleure solution que j'ai trouvée consiste à définir axios.interceptors dans mes principaux composants React et à utiliser that pour gérer les erreurs: (Et avec withRouter à partir du routeur 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');
  }
2
hilderic sb

Cela semble fonctionner pour moi 

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

J'utilise react-router-dom et il a des "historiques" qui peuvent être utilisés dans la transition vers un nouvel itinéraire

 history.Push('/newRoute')
0
pratik

La réponse acceptée ne résout pas mon problème. Après avoir passé du temps dans axios et des tickets autour d’interceptor ne se déclenchant pas, j’ai trouvé qu’axios ne supportait pas la décoration d’intercepteur au niveau mondial, comme il est décrit ci-dessus. Pour les futurs lecteurs, n'oubliez pas qu'axios a identifié ce global interceptor comme fonctionnalité. alors peut-être que nous l'obtiendrons dans le futur. pour ref: https://github.com/axios/axios/issues/993

J'ai une seule instance axios pour tous les appels d'api, j'ai donc résolu de définir un intercepteur. 

0
vishnu lal