Часто на проектах возникают ситуации когда необходимо реализовать свои собственные методы API. Один из ярких примеров с одного из моих проектов: возникла необходимость интегрировать сторонний frontend-интерфейс с ELMA365. В данном случае платформа ELMA365 выступает как backend-сервер. И для интеграции понадобилось создания точек интеграции между двумя системами.
Для общения систем спроектируем свой API. При проектировании своих методов необходимо придерживаться архитектурного стиля REST. Подробнее с этим стилем и теорией по протоколу http предлагаю ознакомиться здесь и на Wikipedia (EN).
Платформа ELMA365 предоставляет удобные инструменты для создания своих методов API. Они реализуются в рамках модулей и ознакомиться с функционалом можно в статье справки. Ниже приведу пример работы с этим функционалом.
Рассмотрим работу с методом GET. Согласно спецификации клиент может передавать параметры выполнения запроса в URI целевого ресурса после символа '?'. Нам необходимо будет разобрать эту строку:
Код:
async function getAppForGuardianship(req: FetchRequest): Promise<HttpResponse | void> {
// Создаем новый экземпляр объекта для ответа клиенту
const response = new HttpResponse()
const phones: string[] = []
// Здесь как раз получаем параметр id из строки запроса
const id = req.query?.['id'] as string
const result = await Global.ns._clients.app._leads.search().size(10000).where(f => f.__id.eq(id)).first()
if (result) {
for (let phone of result.data.contact_phone!) {
phones.push(phone.tel)
}
// "Собираем" наш ответ клиенту
response
.status(200) // Обязательно указываем код статуса! 200 - успех, подробнее по ссылкам в конце статьи
// Собираем тело ответа в виде JSON объекта, stringify применять уже не надо
.json({
msg: 'success',
name: result.data.__name ? result.data.__name : 'name is empty',
parent_fullname: result.data.parent_fullname,
child_fullname: result.data.child_fullname,
child_birthdate: result.data.birthdate!.format('DD.MM.YYYY'),
city: result.data.city,
child_citizenship: result.data.citizenship,
email: result.data.email ? result.data.email.email : 'e-mail не указан',
contact_phone: phones,
pa_diagnosis: result.data.pa_diagnosis
})
.set('Content-Type', 'application/json') // Явно указываем тип содержимого ответа
}
else {
// При разработке REST API важно обрабатывать все ошибки и отдавать клиенту вразумительный ответ, чтобы можно было обработать его допустим на фронте
response
.status(404)
.json({
msg: 'error: App for Guardianship not found'
})
.set('Content-Type', 'application/json')
}
return response
}
Вызов этого метода на клиенте:
Код:
async function getUserDataFromElma() {
const elmaUserId = $('#elma-id').html();
const apiUrl = `https://aeql4vg6lxcee.elma365.ru/api/extensions/3d15932c-766e-4e91-b8ff-fed442649de2/script/get/appgs?id=${elmaUserId}`;
return await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer 8657d620-f5eb-4552-997d-d3ec41688c39',
}
})
}
Теперь рассмотрим работу с методом POST. Согласно спецификации содержимое запроса передается в его теле, т.е. нам необходимо будет разобрать содержимое тела запроса:
Код:
async function createAppForGuardianship(req: FetchRequest): Promise<HttpResponse | void> {
// Создаем новый экземпляр объекта для ответа клиенту
const response = new HttpResponse()
// Клиент передал нам содержимое в виде JSON-пакета, поэтому разберем его в массив
const request = JSON.parse(req.body! as string)
try {
const result = Global.ns._clients.app._leads.create()
result.data.contact_phone = []
result.data.parent_fullname = request['form_submit_data']['parent-name']
result.data.child_fullname = request['form_submit_data']['child-name']
result.data.birthdate = new Datetime(request['form_submit_data']['date-birth'], "d.m.Y").getDate()
result.data.city = request['form_submit_data']['city']
result.data.email = result.fields.email.create(request['form_submit_data']['your-email'])
result.data.contact_phone.push(result.fields.contact_phone.create(request['form_submit_data']['your-number'], PhoneType.Main))
result.data.pa_diagnosis = request['form_submit_data']['diagnosis']
result.data.description = request['form_submit_data']['your-condition']
await result.save()
await result.setStatus(result.fields.__status.groups.wards.variants.new)
// Формируем ответ клиенту
response
.status(200)
.json({
msg: 'success',
id: result.data.__id
})
.set('Content-Type', 'application/json')
}
catch (e) {
// Отдаем ошибку клиенту, если что-то пошло не так
response
.status(500)
.json({
msg: 'error: new app do not create',
})
.set('Content-Type', 'application/json')
}
return response
}
Пример вызова метода на стороне клиента:
Код:
async function updateUserDataFromElma() {
const urlParam = formId ? 'medical' : 'main';
const apiUrl = `https://aeql4vg6lxcee.elma365.ru/api/extensions/3d15932c-766e-4e91-b8ff-fed442649de2/script/ward/${urlParam}/update`;
requestBody = JSON.stringify({
id: userId,
form_id: formId,
context: contextObject.context
})
return await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer 8657d620-f5eb-4552-997d-d3ec41688c39',
},
body: requestBody
})
}
Подведём итог. Для работы со строкой запроса метода GET используем поле FetchRequest.query, а для работы с телом метода POST парсим поле FetchRequest.body как JSON объект. Ниже приведу полное описание полей и методов интерфейсов FetchRequest и HttpResponse.
Код:
req: FetchRequest
req.body - тело запроса, тип string или FormData
req.headers - заголовки запроса, можно разобрать методами get() для типа Record
req.method - тип метода запроса
req.query - строка из самого запроса
const response = new HttpResponse()
response
.content('') - отдать содержимое в виде строки, можно формировать html страницы
.cookie('','') - установить куку, формат (параметр,значение)
.redirect('') - выполнить редирект (важно отдать код из группы 300), можно таким образом поддерживать устаревшие методы
.setFile(file,'') - отдать в теле ответа файл
.status(200) - указать код статуса ответа
.json({}) - сформировать тело ответа в виде JSON объекта
.set('Content-Type', 'application/json')
Особое внимание необходимо уделить адекватным кодам статусов ответов. Ознакомиться с ними можно здесь.