Использование Fetch API в Loginom

8 октября 2026
0 комментариев

В статье рассматривается создание компонента в Loginom на основе JavaScript для выполнения HTTP-запросов с использованием Fetch API и FileStorage API. Показано, как настроить входные и выходные поля, обрабатывать параметры, заголовки и тело запроса, а также реализовать загрузку и выгрузку файлов.

Разберем создание узла для скачивания и выгрузки файлов. Будем использовать компонент JavaScript и его встроенные модули Fetch API и FileStorage API.

Производный компонент

Создадим узел для выполнения набора запросов:

  • Добавим в сценарий узел JavaScript с меткой «fetch» (можно использовать любое удобное имя).
  • Создадим на входном табличном порту строковые поля с назначением Используемое:
    • 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()

Использование

  • Добавим в сценарий производный компонент «fetch» в режиме «Выполнение узла».
Режим «Выполнение узла»

Режим «Выполнение узла»

  • Во входную таблицу добавим поля для формирования Query — параметров выполнения запроса, которые добавляются к URI после знака ?.
    • Для каждого поля установим:
      • Имя.
      • Метку — именно это значение будет использоваться как ключ параметра, так как Имя поля имеет ограничения.
      • Назначение = Элемент:
        • по нему будем определять, что это поле является параметром.
        • Назначение можно задать любое, главное, чтоб его можно было выделить среди других полей.
      • Тип — Строковый или Целый:
        • можно и другие, но JavaScript будет приводить их к строке, формат которой не обязательно будет корректным для сервиса.
  • Добавим поля для формирования Headers — заголовков запроса, метаданных, описывающих его содержимое.
    • Для каждого поля установим (подобно Query):
      • Имя.
      • Метку — именно это значение будет использоваться как имя заголовка.
      • Назначение = Показатель.
      • Тип — Строковый или Целый.
  • Опционально — добавим поля с Назначением Выходное, которые не будут использоватся собственно в запросе, но будут проброшены на выходной порт.
    • К примеру — Идентификатор запроса.
  • Формируем соответствующий датасет и подаем его на входную таблицу.

Входные поля:

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

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

Простой запрос:

Простой запрос
Простой запрос

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

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

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

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

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

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

Другие материалы по теме:

Технические особенности логирования в Loginom

Планировщик задач в Loginom

Смотрите также

Подписывайтесь на телеграмм-канал Loginom
Новости, материалы по аналитике, кейсы применения, активное сообщество
Подписаться