
В статье рассматривается создание компонента в Loginom на основе JavaScript для выполнения HTTP-запросов с использованием Fetch API и FileStorage API. Показано, как настроить входные и выходные поля, обрабатывать параметры, заголовки и тело запроса, а также реализовать загрузку и выгрузку файлов.
Разберем создание узла для скачивания и выгрузки файлов. Будем использовать компонент JavaScript и его встроенные модули Fetch API и FileStorage API.
Создадим узел для выполнения набора запросов:
Используемое:
URI — имя и адрес ресурса.Method — метод HTTP запроса.Body — текстовое содержимое запроса.FilePath — файл для выгрузки. Если значение задано, будет отправлен файл, иначе — содержимое Body.OutputDirectoryPath — каталог для сохранения скачанных файлов. Если значение задано, ответ будет сохранен в файл.OutputFileName — имя скачиваемого файла. Обязательно при заданном OutputDirectoryPath.
Создание на входном табличном порту строковых полей
RequestUrl, строковое — итоговый URL запроса.RequestHeaders, строковое — заголовки запроса.HttpStatusCode, целое — код состояния HTTP.ResponseBody, строковое — тело ответа.ResponseHeaders, строковое — заголовки ответа.ErrorMessage, строковое — описание ошибки при неудачном запросе.FilePath, строковое — итоговый путь к сохраненному файлу — заполняется при успешно завершенном скачивании.
Выходная таблица

Создание производного компонента
Указываем используемые модули:
import { InputTable, OutputTable, UsageType } from "builtIn/Data";
import { fetch, Request, Headers } from "builtIn/Fetch";
import * as fs from "builtIn/FS";
Вспомогательные функции и значения для операций с входными и выходными полями:
// Обязательные входные поля
const UsedColumnsNames = ['URI', 'Method', 'FilePath', 'Body', 'OutputDirectoryPath', 'OutputFileName']
const UsedColumns = UsedColumnNames.map(name => InputTable.GetColumn(name))
const InputColumns = Array.from(InputTable.Columns); // все входные поля
const HeadersColumns = InputColumns.filter(col => col.UsageType == UsageType.Value) // 7 Показатель
const HeadersColumnsDisplayNames = HeadersColumns.map(col => col.DisplayName)
const QueryColumns = InputColumns.filter(col => col.UsageType == UsageType.Item) // 9 Элемент
const QueryColumnsDisplayNames = QueryColumns.map(col => col.DisplayName)
const ForwardedColumns = InputColumns.filter(col => col.UsageType == UsageType.Output) // 4 Выходные
function GetRow(i, columns, names) {
return columns.reduce(
(row, col, colIndex) => { row[names[colIndex]] = col.Get(i); return row }, {}
)
}
// Обязательные выходные поля
const OutColumns = ['RequestUrl', 'RequestHeaders', 'HttpStatusCode', 'ResponseBody', 'ResponseHeaders', 'ErrorMessage', 'FilePath']
const Out = OutColumns.reduce(
(obj, item) => { obj[item] = OutputTable.GetColumn(item); return obj }, {}
)
const OutColumnsCount = OutColumns.length
function ForwardValues(i) {
ForwardedColumns.forEach((col,colIndex) => {
OutputTable.Set(OutColumnsCount + colIndex, col.Get(i))
})
}
Добавляем функцию для получения строки параметров запроса:
Параметр будет добавлен, если его значение не равно null и не является пустой строкой ('').
function Query(i) {
const params = []
QueryColumns.forEach((col, colIndex) => {
const value = col.Get(i)
if (null != value && '' !== value) params.push(QueryColumnsDisplayNames[colIndex] + '=' + encodeURIComponent(value.toString()))
})
return params.length == 0 ? null : params.join('&')
}
Добавляем функцию для получения объекта, соответствующего заголовкам запроса:
Заголовок также будет добавлен, если его значение не равно null и не является пустой строкой ('').
function InputHeaders(i) {
const obj = {}
HeadersColumns.forEach((col, colIndex) => {
const value = col.Get(i)
if (null != value && '' !== value) obj[HeadersColumnsDisplayNames[colIndex]] = value
})
return obj
}
Формирование объекта запроса:
function NewRequest(url, method, headers, content) {
const requestInit = {
method: method,
headers: new Headers(headers),
redirect: "follow"
}
if (content) requestInit.body = content
return new Request(url, requestInit)
}
Заголовки в виде JSON для выходных данных:
function HeadersAsJSON(headers) {
const obj = {}
headers.forEach(function (value, key) {
obj[key] = value
});
return JSON.stringify(obj, null, 2)
}
Создание каталога загрузки, если не существует:
function MkDir(path) {
const realPath = fs.realpathSync(path)
if (!fs.existsSync(realPath))
fs.mkdirSync(realPath, { recursive: true })
}
Основная функция для обработки строк входной таблицы. Для каждой строки формируется и выполяется запрос, результаты добавляются в выходную таблицу:
async function RowsHandle() {
const n = InputTable.RowCount
for (let i = 0; i < n; i++) {
const row = GetRow(i, UsedColumns, UsedColumnsNames)
const queryString = Query(i)
// добавляем выходную строку
OutputTable.Append()
try {
// создаем запрос
const request = NewRequest(
row.URI + (queryString ? '?' + queryString : ''),
row.Method,
new InputHeaders(i),
row.FilePath ?
// содержимое запроса читаем из файла, если задан его путь
fs.readFileSync(row.FilePath) :
// иначе - строковое содержимое из поля Body
row.Body
)
// фиксируем в результатах запрошенный url
Out.RequestUrl.Set(request.url)
// и заголовки запроса
Out.RequestHeaders.Set(HeadersAsJSON(request.headers))
// выполняем запрос
const response = await fetch(request)
// фиксируем http код
Out.HttpStatusCode.Set(response.status)
// заголовки
Out.ResponseHeaders.Set(HeadersAsJSON(response.headers))
// и содержимое ответа
// если задан выходной каталог
if (row.OutputDirectoryPath) {
// сохраняем скачанный файл
if (response.status < 300) {
MkDir(row.OutputDirectoryPath)
const path = fs.realpathSync(row.OutputDirectoryPath + '/' + row.OutputFileName)
fs.writeFileSync(path, await response.arrayBuffer())
Out.FilePath.Set(path)
} else {
Out.ResponseBody.Set(await response.text())
Out.ErrorMessage.Set(response.statusText)
}
} else {
// иначе — фиксируем текст ответа
Out.ResponseBody.Set(await response.text())
if (response.status >= 400) {
Out.ErrorMessage.Set(response.statusText)
}
}
} catch (e) {
Out.ErrorMessage.Set(e.message)
}
// включаем также "пробрасываемые" поля
ForwardValues(i)
}
}
Добавляем пробрасываемые поля в выходную таблицу:
ForwardedColumns.forEach(col => {
OutputTable.AddColumn(col)
})
И запускаем обработку строк:
RowsHandle()

Режим «Выполнение узла»
?.
Элемент:
Строковый или Целый:
Показатель.Строковый или Целый.Выходное, которые не будут использоватся собственно в запросе, но будут проброшены на выходной порт.
Входные поля:

Настройка входных полей
Простой запрос:


Выгрузка файла:

Выгрузка файла

Выгрузка файла из Loginom
Теперь можно использовать Производный компонент для автоматизированного взаимодействия с внешними HTTP-сервисами, включая загрузку и сохранение файлов в Loginom.
Другие материалы по теме: