113 lines
4.7 KiB
JavaScript
113 lines
4.7 KiB
JavaScript
import axios from 'axios';
|
||
import authServiceApi from './AuthServiceAPI';
|
||
import tokenApi from './TokenAPI';
|
||
|
||
/**
|
||
* @interface RequestConfig
|
||
* @type {Object}
|
||
* @property {MethodType} method - метод запроса
|
||
* @property {string} url - адрес запроса
|
||
* @property {Object} body - тело запроса
|
||
*/
|
||
|
||
/**
|
||
* Api прикрепляет ко всем запросом заголовок с токеном авторизации. В случае, когда сервер
|
||
* возвращает ошибку 401 (Не авторизован), пытается рефрешнуть токен. В момент обновления пары токенов
|
||
* api сохраняет все запросы в очередь. Если ревреш токенов удался, то запросы будут выполнены
|
||
* в том же порядке, как и пришли, либо упадут с ошибкой, если ревреш выполнить не удалось.
|
||
* @class HttpAuthApi
|
||
*/
|
||
class HttpAuthApi {
|
||
constructor () {
|
||
this.pendingRequests = [];
|
||
this.isPendingRefresh = false;
|
||
}
|
||
|
||
/**
|
||
* Выполняет какое-то действие над всеми запросами из очереди, пока она не закончится
|
||
* @param {Function} handler - функция обработчик запроса
|
||
*/
|
||
processPendingRequests = (handler) => {
|
||
this.isPendingRefresh = false;
|
||
while (this.pendingRequests.length) {
|
||
const pendingRequest = this.pendingRequests.shift();
|
||
handler(pendingRequest);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Повторяет все запросы из очереди в порядке их поступления
|
||
*/
|
||
repeatRequests = () => {
|
||
this.processPendingRequests(({resolve, reject, requestConfig}) => {
|
||
this.request(requestConfig)
|
||
.then(resolve)
|
||
.catch(reject);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Выбрасывает ошибки для всех запросов из очереди
|
||
* @param {axios.error} error - ошибка, которую возвращает axios
|
||
*/
|
||
rejectRequests = (error) => {
|
||
this.processPendingRequests(({reject}) => {
|
||
reject(error);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Перехватывает ошибку 401 (Не авторизован), другие ошибки выбрасывает дальше
|
||
* @param {axios.error} error - ошибка, которую возвращает axios
|
||
* @param {Function} resolve - функция промиса, которая разрешает его успешно
|
||
* @param {Function} reject - функция промиса, которая завершает его неудачей
|
||
* @param {RequestConfig} requestConfig - настройки для выполнения запроса
|
||
*/
|
||
errorHandling = (error, resolve, reject, requestConfig) => {
|
||
if (error.response.status === 401) {
|
||
this.isPendingRefresh = true;
|
||
this.pendingRequests({resolve, reject, requestConfig});
|
||
authServiceApi.refresh()
|
||
.then(() => {
|
||
this.repeatRequests();
|
||
})
|
||
.catch((refreshError) => {
|
||
this.rejectRequests(refreshError);
|
||
/*
|
||
* Снять авторизацию и сделать роут
|
||
* Сбросить все запросы с ошибкой авторизации
|
||
*/
|
||
});
|
||
} else {
|
||
reject(error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Добавляет к запросу токен в качестве заголовка
|
||
* @param {RequestConfig} requestConfig - настройки для выполнения запроса
|
||
*/
|
||
addTokenToRequest = (requestConfig) => ({
|
||
...requestConfig,
|
||
headers: {
|
||
'Authorization': tokenApi.getAccessToken(),
|
||
},
|
||
})
|
||
|
||
/**
|
||
* Метод для внешнего использования, выполняет запрос к серверу, используя axios
|
||
* @param {RequestConfig} requestConfig - настройки для выполнения запроса
|
||
*/
|
||
request = (requestConfig) => {
|
||
return new Promise((resolve, reject) => {
|
||
axios(this.addTokenToRequest(requestConfig))
|
||
.then((response) => resolve(response))
|
||
.catch((error) => this.errorHandling(error, resolve, reject, requestConfig));
|
||
});
|
||
}
|
||
}
|
||
|
||
const httpAuthApi = new HttpAuthApi();
|
||
|
||
export default httpAuthApi;
|