Как перенаправить с перехватчика axios с помощью react 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);
});
6 ответов
я решил это, обратившись к моему магазину Redux извне дерева компонентов и отправив ему то же самое действие с кнопки выхода из системы, так как мои перехватчики создаются в отдельном файле и загружаются до загрузки любого компонента.
Итак, я сделал следующее:
At :
//....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'));
и :
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, он устанавливает my isLogged
собственность в my 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);
}
надеюсь, что это поможет!
я решил эту задачу, создав историю браузера от 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
С react-router
(https://github.com/ReactTraining/react-router) и проходить одни и те же истории объект как history
парам.
// app.js
...
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
...
</Router>
</Provider>
, document.getElementById('#root'))
надеюсь, это поможет.
лучшее решение, которое я нашел, - определить axios.перехватчики внутри моих основных компонентов React и use that
для обработки ошибок :
( И с withRouter
роутер В4 )
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');
}
Это, кажется, работает для меня
function (error) {
var accessDenied = error.toString().indexOf("401");
if (accessDenied !== -1) {
console.log('ACCESS DENIED')
return window.location.href = '/accessdenied'
}
});
Я использую react-router-dom, и у него есть реквизит" история", который можно использовать при переходе на новый маршрут
history.push('/newRoute')
здесь я делаю это так:
.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 / network-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/история.js:
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
и на главном маршруте я также называю историю:
...
<Router history={history}>
...