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);
});
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!
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!
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');
}
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'
}
});
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')
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.