Отправка GET-запроса к внешнему API из 1С 8.3

· 10 мин

Содержание

GET-запрос из 1С отправляют тремя объектами: HTTPСоединение (куда подключаемся), HTTPЗапрос (что запрашиваем) и метод Получить(), который возвращает HTTPОтвет. Тело ответа чаще всего приходит в JSON — его читают через объект ЧтениеJSON и функцию ПрочитатьJSON.

Ниже — рабочий код на встроенном языке, который подключается к внешнему API по HTTPS, забирает данные, разбирает JSON в Соответствие и корректно реагирует на коды ответа и сбои сети. Весь код выполняется на стороне сервера или в фоновом задании: прямое обращение к внешним ресурсам с клиента в большинстве случаев недоступно.

Что понадобится и из каких объектов собирается запрос

Для обращения к HTTP-сервису достаточно штатной платформы 8.3 — никаких внешних компонент. Код размещают в серверной процедуре общего модуля (флаг Сервер) либо в модуле объекта.

Три кирпича, из которых строится любой запрос:

  • 🔌 HTTPСоединение — адрес сервера, порт, таймаут и признак защищённого соединения.
  • 📨 HTTPЗапрос — путь к ресурсу (без домена) и заголовки.
  • 📥 HTTPОтвет — то, что вернул метод Получить(): код состояния, заголовки и тело.

Важно сразу разделить адрес на части. В конструктор HTTPСоединение идёт только домен — без https:// и без пути. А путь с параметрами (всё, что после домена) передаётся в HTTPЗапрос.

Для HTTPS в последний значимый параметр конструктора передавайте Новый ЗащищенноеСоединениеOpenSSL. Без него подключение к адресу на 443-м порту завершится ошибкой проверки сертификата.

Порт зависит от протокола: 443 для HTTPS, 80 для обычного HTTP. Если порт не указать явно, платформа подставит значение по умолчанию, но лучше задавать его осознанно.

Собираем и отправляем GET-запрос

Минимальный рабочий пример — подключение к API и получение тела ответа строкой:

Функция ПолучитьЗаказы()

    Соединение = Новый HTTPСоединение(
        "api.example.com",                  // только домен, без http:// и пути
        443,                                // 443 — HTTPS, 80 — HTTP
        ,                                   // пользователь
        ,                                   // пароль
        ,                                   // прокси
        30,                                 // таймаут, секунд
        Новый ЗащищенноеСоединениеOpenSSL); // обязательно для HTTPS

    Запрос = Новый HTTPЗапрос("/v1/orders?status=new");
    Запрос.Заголовки.Вставить("Accept", "application/json");

    Ответ = Соединение.Получить(Запрос);

    Возврат Ответ;

КонецФункции

Разберём по шагам, что здесь происходит:

  1. Создаём соединение с доменом api.example.com. Таймаут 30 секунд — разумная граница: если сервер молчит дольше, запрос прервётся с исключением, а не повиснет.
  2. Создаём запрос и в адресе ресурса указываем путь /v1/orders вместе со строкой параметров ?status=new.
  3. Через коллекцию Заголовки добавляем Accept: application/json — сообщаем серверу, что ждём JSON.
  4. Вызываем Соединение.Получить(Запрос). Именно метод Получить() соответствует HTTP-методу GET.

Для других методов у объекта есть свои функции: ОтправитьДляОбработки() — это POST, Записать() — PUT, Удалить() — DELETE. Нас интересует GET, поэтому работаем с Получить().

Обращение к внешнему адресу из кода на клиенте платформа блокирует — выполняйте запрос на сервере. Если нужно показать результат пользователю, оберните серверный вызов в процедуру с директивой &НаСервере или запустите в фоновом задании для долгих выгрузок.

Читаем ответ сервера и разбираем JSON

У объекта HTTPОтвет три части, которые нужны почти всегда:

Свойство / методЧто возвращает
КодСостоянияЧисло: 200 — успех, 404 — не найдено, 401 — нет авторизации
ПолучитьТелоКакСтроку("UTF-8")Тело ответа строкой в указанной кодировке
ПолучитьТелоКакДвоичныеДанные()Тело бинарно — для файлов, картинок, архивов
ЗаголовкиСоответствие заголовков ответа

Сначала всегда проверяют код состояния, и только при успехе разбирают тело. Чтение JSON идёт через отдельный объект:

Если Ответ.КодСостояния <> 200 Тогда
    ВызватьИсключение "Сервер вернул код " + Ответ.КодСостояния;
КонецЕсли;

ТелоСтрокой = Ответ.ПолучитьТелоКакСтроку("UTF-8");

Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТелоСтрокой);
Данные = ПрочитатьJSON(Чтение);   // по умолчанию вернёт Соответствие
Чтение.Закрыть();

Функция ПрочитатьJSON по умолчанию возвращает Соответствие — к значениям обращаются по ключу: Данные["id"], Данные["name"]. Если передать вторым параметром Ложь, платформа попытается собрать Структуру, но это сработает только когда все имена свойств — корректные идентификаторы (без пробелов, дефисов и цифры в начале). Надёжнее оставлять Соответствие.

Когда сервер возвращает массив объектов, обход выглядит так:

Для Каждого Строка Из Данные Цикл
    Сообщить(Строка["number"] + " — " + Строка["sum"]);
КонецЦикла;

Всегда указывайте кодировку в ПолучитьТелоКакСтроку. Без параметра платформа берёт кодировку из заголовка Content-Type, а если сервер его не прислал — результат получится с «кракозябрами». Для большинства современных API это UTF-8.

Объект ЧтениеJSON после работы закрывают методом Закрыть() — он освобождает поток.

Параметры запроса и кодирование URL

Параметры GET-запроса передаются прямо в строке ресурса после знака ?, пары ключ=значение разделяются амперсандом &. Пока значения состоят из латинских букв и цифр, их можно писать как есть.

Проблемы начинаются, когда в параметр попадают пробелы, кириллица или спецсимволы (&, =, +, /). Такую строку нужно закодировать функцией КодироватьСтроку:

ИмяДляПоиска = КодироватьСтроку("Иванов Иван", СпособКодированияСтроки.КодировкаURL);
Запрос = Новый HTTPЗапрос("/search?name=" + ИмяДляПоиска + "&limit=50");

Кодируют значения параметров по отдельности, а не весь адрес целиком — иначе разделители ?, = и & тоже превратятся в коды и сервер не разберёт строку.

💡 Удобный приём для нескольких параметров — собирать их в цикле:

Параметры = Новый Структура;
Параметры.Вставить("name",  "Иванов Иван");
Параметры.Вставить("city",  "Москва");

ЧастиАдреса = Новый Массив;
Для Каждого КлючЗначение Из Параметры Цикл
    Значение = КодироватьСтроку(КлючЗначение.Значение, СпособКодированияСтроки.КодировкаURL);
    ЧастиАдреса.Добавить(КлючЗначение.Ключ + "=" + Значение);
КонецЦикла;

АдресРесурса = "/search?" + СтрСоединить(ЧастиАдреса, "&");
Запрос = Новый HTTPЗапрос(АдресРесурса);

Так адрес соберётся корректно при любом числе параметров, а каждое значение будет закодировано ровно один раз.

Авторизация: Basic, токен Bearer и заголовки

Закрытые API требуют авторизации. Способ зависит от сервиса, но большинство укладывается в два варианта, и оба передаются заголовком Authorization.

Токен Bearer — самый частый вариант у современных REST API. Токен выдаётся в личном кабинете сервиса и просто подставляется в заголовок:

Запрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);

Basic-авторизация — логин и пароль, склеенные через двоеточие и закодированные в Base64:

СтрокаДоступа = "user:password";
Двоичные     = ПолучитьДвоичныеДанныеИзСтроки(СтрокаДоступа, "UTF-8");
Код64        = СтрЗаменить(Base64Строка(Двоичные), Символы.ПС, "");
Запрос.Заголовки.Вставить("Authorization", "Basic " + Код64);

Функция Base64Строка может вставлять переносы строк в длинный результат, поэтому их убирают СтрЗаменить(..., Символы.ПС, ""). Логин и пароль для Basic можно задать и прямо в конструкторе HTTPСоединение (3-й и 4-й параметры) — платформа сама сформирует заголовок.

Помимо авторизации серверу часто нужны дополнительные заголовки:

  • Accept — формат ответа, который мы ждём (application/json).
  • User-Agent — некоторые серверы отклоняют запросы без него.
  • X-Api-Key или другой нестандартный заголовок — если так требует документация сервиса.

Токены и пароли не храните в тексте модуля в открытом виде. Выносите их в константу, защищённое хранилище или параметры сеанса. Код с логином попадает в выгрузку конфигурации и в историю хранилища — это утечка доступа.

Обрабатываем ошибки и отлаживаем соединение

Сетевой запрос падает по двум разным причинам, и реагировать на них нужно по-разному.

Первая — исключение. Сервер недоступен, истёк таймаут, не прошла проверка сертификата. Метод Получить() в этом случае вызывает исключение, поэтому вызов оборачивают в Попытка:

Попытка
    Ответ = Соединение.Получить(Запрос);
Исключение
    Текст = ОписаниеОшибки();
    ЗаписьЖурналаРегистрации("Обмен.API", УровеньЖурналаРегистрации.Ошибка,,, Текст);
    ВызватьИсключение "Не удалось подключиться к сервису: " + Текст;
КонецПопытки;

Вторая — соединение прошло, но сервер ответил ошибкой. Здесь исключения не будет: запрос считается выполненным, а о проблеме говорит КодСостояния.

КодЧто означаетЧто проверить
200УспехРазбираем тело
301 / 302ПереадресацияПрочитать заголовок Location и повторить запрос по новому адресу
400Неверный запросПараметры и формат адреса
401Нет авторизацииТокен или логин в заголовке Authorization
403Доступ запрещёнПрав токена не хватает для этого ресурса
404Ресурс не найденПуть после домена, опечатки в адресе
500–503Сбой на стороне сервераПовторить позже, проверить статус сервиса

Платформа не переходит по редиректам 301/302 автоматически. Если сервис отвечает такими кодами, нужно вручную взять адрес из заголовка Location и отправить запрос заново.

Для отладки пригодятся простые приёмы:

  • При любом коде читайте тело через ПолучитьТелоКакСтроку("UTF-8") — сервер обычно присылает текст ошибки с пояснением (например, invalid token или rate limit exceeded).
  • Пишите код ответа и тело в журнал регистрации — это быстрее, чем ловить сбой в бою.
  • Проверьте тот же адрес в браузере или через curl: если и там ошибка, проблема не в 1С, а в API или доступе.
  • Для сложных случаев поднимите локальный прокси (Fiddler, Charles) и укажите его в параметре «прокси» конструктора — увидите реальный трафик запроса и ответа.

Готовая диагностика по частым симптомам:

Диагностика: выберите симптом

Постраничная выборка: когда данных много

API редко отдаёт всё одним ответом. Большие списки разбиваются на страницы, и за них отвечают параметры вроде page и limit (или offset и count — смотрите документацию конкретного сервиса). Признак того, что страницы кончились, сервис обычно возвращает в теле ответа: пустой массив, поле has_more: false или общее число записей.

Выборку собирают циклом, увеличивая номер страницы, пока сервер отдаёт данные:

Результат = Новый Массив;
Страница  = 1;

Пока Истина Цикл

    Адрес  = "/v1/orders?limit=100&page=" + Страница;
    Запрос = Новый HTTPЗапрос(Адрес);
    Запрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);

    Ответ = Соединение.Получить(Запрос);
    Если Ответ.КодСостояния <> 200 Тогда
        Прервать;
    КонецЕсли;

    Чтение = Новый ЧтениеJSON;
    Чтение.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку("UTF-8"));
    Порция = ПрочитатьJSON(Чтение);
    Чтение.Закрыть();

    Если Порция.Количество() = 0 Тогда
        Прервать;     // страницы закончились
    КонецЕсли;

    Для Каждого Элемент Из Порция Цикл
        Результат.Добавить(Элемент);
    КонецЦикла;

    Страница = Страница + 1;

КонецЦикла;

Добавляйте условие выхода из цикла и по номеру страницы (например, не больше 1000 итераций). Если сервер из-за сбоя всегда будет отдавать непустой ответ, бесконечный цикл повесит фоновое задание.

Для тяжёлых выгрузок такой перебор запускают в фоновом задании, а не в клиентском сеансе — пользователю не придётся ждать завершения у открытой формы.

Полный пример: запрос с токеном и разбором ошибок

Соберём всё вместе — функцию, которая подключается по HTTPS, авторизуется токеном, проверяет код ответа и возвращает разобранные данные. Такую заготовку удобно держать в общем модуле и переиспользовать:

Функция ЗапроситьДанные(ПутьРесурса, Токен) Экспорт

    Соединение = Новый HTTPСоединение(
        "api.example.com", 443, , , , 30,
        Новый ЗащищенноеСоединениеOpenSSL);

    Запрос = Новый HTTPЗапрос(ПутьРесурса);
    Запрос.Заголовки.Вставить("Accept", "application/json");
    Запрос.Заголовки.Вставить("Authorization", "Bearer " + Токен);

    Попытка
        Ответ = Соединение.Получить(Запрос);
    Исключение
        ЗаписьЖурналаРегистрации("Обмен.API",
            УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
        ВызватьИсключение "Сервис недоступен: " + ОписаниеОшибки();
    КонецПопытки;

    ТелоСтрокой = Ответ.ПолучитьТелоКакСтроку("UTF-8");

    Если Ответ.КодСостояния <> 200 Тогда
        ВызватьИсключение "Код " + Ответ.КодСостояния + ": " + ТелоСтрокой;
    КонецЕсли;

    Чтение = Новый ЧтениеJSON;
    Чтение.УстановитьСтроку(ТелоСтрокой);
    Данные = ПрочитатьJSON(Чтение);
    Чтение.Закрыть();

    Возврат Данные;

КонецФункции

Обратите внимание на порядок: тело читают до проверки кода состояния, чтобы при ошибке подложить в исключение текст от сервера. Это экономит время на разборе: вместо сухого «код 401» в журнале окажется invalid or expired token.

Перед первым запуском к новому сервису пройдитесь по короткому списку — он снимает большинство причин, по которым запрос не доходит:

Перед первым запросом к API
Выполнено: 0 / 6

Частые вопросы

Можно ли отправить GET-запрос с клиента, а не с сервера?
В типовой схеме — нет. Обращение к внешним ресурсам выполняется на сервере или в фоновом задании. С клиента вызывают серверную процедуру, которая и делает запрос.
Чем GET отличается от POST в коде 1С?
Методом объекта HTTPСоединение: GET — это Получить(), POST — ОтправитьДляОбработки(). У GET данные передаются в строке адреса, у POST — в теле запроса.
Как передать несколько параметров в GET-запросе?
Через знак ? и разделитель &: /search?name=...&city=...&limit=50. Каждое значение по отдельности кодируйте функцией КодироватьСтроку с видом КодировкаURL.
Почему приходит пустой ответ, хотя код состояния 200?
Чаще всего тело читают не той кодировкой или до проверки КодСостояния уже произошло исключение. Прочитайте тело строкой с "UTF-8" и выведите его в сообщение или журнал.
Нужны ли внешние компоненты для работы с HTTP?
Нет. Объекты HTTPСоединение, HTTPЗапрос, ЧтениеJSON входят в штатную платформу 8.3 — ничего доустанавливать не требуется.