Как перенаправить с перехватчика 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/история