В процессе работы из проекта в проект я тащу свой «велосипед», постоянного его подтачиваю или модифицирую под конкретные нужды. Хотел бы рассказать о нем, его плюсах и минусах. Кроме этого, поговорим о том, какие «велосипеды» для работы с API существуют.
27. - Обработка неудачных запросов.
- Отдельная обработка для разных групп запросов.
- Повторное выполнение запросов после выполнения некоторых
действий
Что еще можно делать
30. - В теле запроса.
- В параметрах запроса. В url ресурса.
- Как часть URL. Идентификаторы ресурсов.
где передаются параметры запроса.
Параметры запроса
31. внешний код абстрагирован от этой логики.
getCourse: {method: 'get', url: 'api/v1/courses/{courseId}'},
/**
* Получение текущего курса.
*/
export async function getCurrentCourse({rootGetters, commit, dispatch}, courseId) {
try {
const data = rootGetters.api.getCourse({params: {courseId}})
commit('setCurrentCourse', data)
} catch (err) {
dispatch('common/errorMessage', 'errors.fetchCoursesError', {root: true})
throw Error(err)
}
}
Внешний код абстрагирован
33. /**
* Прокси объект для динамического вызова функций апи.
*/
export default new Proxy(
new ApiClientClass(),
{
get: function (target, name) {
if (BACKEND_ENDPOINTS[name] !== undefined) {
return ({params = {}, data = {}, args = {}} = {}) => {
return target.client({
method: BACKEND_ENDPOINTS[name].method,
url: target.urlFormat(BACKEND_ENDPOINTS[name].url, args),
data: data,
params: params
})
.then((serverResponse) => {data: serverResponse.data})
.catch((error) => {
if (error.response.status === httpBadRequest)
new BadDataError('Bad request error')
throw new Error('Server response error')
})
}
} else {
// Если вызов не относиться к вызову стандартного API вызываем его напрямую из объекта
return target[name]
}
}
}
)
Универсальный метод
34. /**
* Прокси объект для динамического вызова функций апи.
*/
export default new Proxy(
new ApiClientClass(),
{
get: function (target, name) {
if (BACKEND_ENDPOINTS[name] !== undefined) {
return ({params = {}, data = {}, args = {}} = {}) => {
return target.client({
method: BACKEND_ENDPOINTS[name].method,
url: target.urlFormat(BACKEND_ENDPOINTS[name].url, args),
data: data,
params: params
})
.then((serverResponse) => {data: serverResponse.data})
.catch((error) => {
if (error.response.status === httpBadRequest)
new BadDataError('Bad request error')
throw new Error('Server response error')
})
}
} else {
// Если вызов не относиться к вызову стандартного API вызываем его напрямую из объекта
return target[name]
}
}
}
)
Универсальный метод
35. /**
* Прокси объект для динамического вызова функций апи.
*/
export default new Proxy(
new ApiClientClass(),
{
get: function (target, name) {
if (BACKEND_ENDPOINTS[name] !== undefined) {
return ({params = {}, data = {}, args = {}} = {}) => {
return target.client({
method: BACKEND_ENDPOINTS[name].method,
url: target.urlFormat(BACKEND_ENDPOINTS[name].url, args),
data: data,
params: params
})
.then((serverResponse) => {data: serverResponse.data})
.catch((error) => {
if (error.response.status === httpBadRequest)
new BadDataError('Bad request error')
throw new Error('Server response error')
})
}
} else {
// Если вызов не относиться к вызову стандартного API вызываем его напрямую из объекта
return target[name]
}
}
}
)
Универсальный метод
36. /**
* Прокси объект для динамического вызова функций апи.
*/
export default new Proxy(
new ApiClientClass(),
{
get: function (target, name) {
if (BACKEND_ENDPOINTS[name] !== undefined) {
return ({params = {}, data = {}, args = {}} = {}) => {
return target.client({
method: BACKEND_ENDPOINTS[name].method,
url: target.urlFormat(BACKEND_ENDPOINTS[name].url, args),
data: data,
params: params
})
.then((serverResponse) => {data: serverResponse.data})
.catch((error) => {
if (error.response.status === httpBadRequest)
new BadDataError('Bad request error')
throw new Error('Server response error')
})
}
} else {
// Если вызов не относиться к вызову стандартного API вызываем его напрямую из объекта
return target[name]
}
}
}
)
Универсальный метод
37. реализация универсального метода
/**
* Прокси объект для динамического вызова функций апи.
*/
export default new Proxy(
new ApiClientClass(),
{
get: function (target, name) {
if (BACKEND_ENDPOINTS[name] !== undefined) {
return ({params = {}, data = {}, args = {}} = {}) => {
return target.client({
method: BACKEND_ENDPOINTS[name].method,
url: target.urlFormat(BACKEND_ENDPOINTS[name].url, args),
data: data,
params: params
})
.then((serverResponse) => {data: serverResponse.data})
.catch((error) => {
if (error.response.status === httpBadRequest)
new BadDataError('Bad request error')
throw new Error('Server response error')
})
}
} else {
// Если вызов не относиться к вызову стандартного API вызываем его напрямую из объекта
return target[name]
}
}
}
)
Универсальный метод
38. /**
* Прокси объект для динамического вызова функций апи.
*/
export default new Proxy(
new ApiClientClass(),
{
get: function (target, name) {
if (BACKEND_ENDPOINTS[name] !== undefined) {
return ({params = {}, data = {}, args = {}} = {}) => {
return target.client({
method: BACKEND_ENDPOINTS[name].method,
url: target.urlFormat(BACKEND_ENDPOINTS[name].url, args),
data: data,
params: params
})
.then((serverResponse) => {data: serverResponse.data})
.catch((error) => {
if (error.response.status === httpBadRequest)
new BadDataError('Bad request error')
throw new Error('Server response error')
})
}
} else {
// Если вызов не относиться к вызову стандартного API вызываем его напрямую из объекта
return target[name]
}
}
}
)
Универсальный метод
39. /**
* Прокси объект для динамического вызова функций апи.
*/
export default new Proxy(
new ApiClientClass(),
{
get: function (target, name) {
if (BACKEND_ENDPOINTS[name] !== undefined) {
return ({params = {}, data = {}, args = {}} = {}) => {
return target.client({
method: BACKEND_ENDPOINTS[name].method,
url: target.urlFormat(BACKEND_ENDPOINTS[name].url, args),
data: data,
params: params
})
.then((serverResponse) => {data: serverResponse.data})
.catch((error) => {
if (error.response.status === httpBadRequest)
new BadDataError('Bad request error')
throw new Error('Server response error')
})
}
} else {
// Если вызов не относиться к вызову стандартного API вызываем его напрямую из объекта
return target[name]
}
}
}
)
Универсальный метод
40. // Получение токенов при помощи авторизационных данных пользователя.
async loginByToken ({ urlArguments }) {
try {
const result = await this.client({
method: BACKEND_ENDPOINTS.updateToken.method,
url: this.urlFormat(BACKEND_ENDPOINTS.updateToken.url, urlArguments)
})
const { data } = result
this.setTokens({
tAccess: data.jwt_token.access,
tRefresh: data.jwt_token.refresh
})
return true
} catch (err) {
if (err.response.status === httpNotFound) {
// Пробуем определить является ли ответ бизнес ответом или это просто не валидный урл
if (err.response.data.result) {
console.warn('Token on server not found err.response = ', err.response)
return false
}
throw new Error(err)
}
throw new Error(err)
}
}
Специальные методы
41. async logout () {
try {
const result = await this.client({
...{
method: BACKEND_ENDPOINTS.updateToken.method,
url: `${BACKEND_ENDPOINTS.updateToken.url}${token}/`
}
})
} catch (error) {
if (error !== USER_UNAUTHORIZED) {
const respStatus = error.response.status
if (![httpForbidden, httpUnauthorized].includes(respStatus)) {
throw new Error(error.response)
}
}
} finally {
this.removeTokens()
}
}
Специальные методы
45. - Обработки ответов опирается на JSON-RPC коды
- Упрощенная логика формирования запроса
- Упрощенная логика обработки ошибок
- Возможность множественного вызова
JSON-RPC vs REST-like