How to redirect from axios interceptor using Router V4?

I want to redirect in axios interceptors when I receive error 403. But how can I access history outside of React components?

In Software Navigation in React-Router v4 , this is in the context of the React component, but here I am trying in the context of 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); }); 
+12
source share
9 answers

I decided that by contacting my Redux store from behind the Component tree and sending it the same action from the exit button, since my interceptors are created in a separate file and loaded before any component loads.

So basically, I did the following:

In the index.js file:

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

And in the network-service.js file:

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

And last but not least, I have a HOC (higher order component) that I wrap with protected components, where I do the actual redirection when the session is absent. Thus, when I run the action type UNAUTH_USER, it sets my isLogged property in my session reducer to false , and therefore this component receives a notification and makes a redirect for me at any time.

File for 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); } 

Hope this helps!

+22
source

I solved this problem by creating a browser history from the history package ( https://github.com/ReactTraining/history ) and passing it to the interceptor function, and then calling the .push() method from it.

The main code of the file (part of it):

 // app.js import createHistory from 'history/createBrowserHistory'; import httpService from './api_client/interceptors'; ... const history = createHistory(); httpService.setupInterceptors(store, history); 

Interceptor Configuration:

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

In addition, you should use Router from react-router ( https://github.com/ReactTraining/react-router ) and pass the same history object to the history parameter.

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

Hope this helps!

+11
source

The best solution I've found is to define axios.interceptors inside my main React components and use that to handle errors: (And with withRouter from 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'); } 
+2
source

Have you tried making <Redirect> ? This is the standard redirection method with v4. I'm not sure where your code lives, but while you import React, it should work.

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

Here's the documentation for <Redirect>

+1
source

It seems to work for me

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

I use response-router-dom and has the props "history", which can be used when switching to a new route

  history.push('/newRoute') 
0
source

Here I do it like this:

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

/ _ helpers / networks 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); }); } }; 

/ _ helpers /history.js:

 import { createBrowserHistory } from 'history'; export const history = createBrowserHistory(); 

and on the main route I also call the story:

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

The accepted answer does not solve my problem. After spending time in axios and tickets around an interceptor that doesn't fire, I found that axios does not support the global interceptor design as described above. for future readers, please keep in mind that axios marked this global interceptor as a function. so maybe we will get it in the future. for reference: https://github.com/axios/axios/issues/993 .

I have one instance of axios for all API calls, so I decided to define an interceptor in it.

0
source
 import { createHashHistory } from 'history' // or createBrowserHistory const history = createHashHistory() if (response.status === 403) { history.push('/login') } 

ReactTraining / History

0
source

Source: https://habr.com/ru/post/1266318/


All Articles