Как отправить HTTPS REST запрос с параметрами form-data из 1С, избежав ошибки 403?

Программист 1С v8.3 (Управляемые формы)
← На главную

При интеграции 1С с внешними REST API через HTTPS часто возникает задача отправки данных в формате form-data. Это может быть необходимо для передачи логина и пароля, других ключевых параметров или даже файлов. Однако, как показывает практика (для начинающих разработчиков будет полезен интерактивный обучающий курс по работе с HTTP-запросами), некорректное формирование такого запроса из 1С может приводить к ошибкам, например, к ошибке 403 (Forbidden) с сообщением о недопустимых параметрах.

В этой статье мы подробно разберем, как правильно сформировать и отправить HTTPS REST запрос с параметрами form-data из 1С, используя объекты HTTPСоединение и HTTPЗапрос. Подобные механизмы лежат в основе сложных систем, таких как подсистема импорта задач из Jira в 1С через REST API, где требуется надежный обмен данными.

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

Предположим, мы отправляем запрос к внешнему API, передавая параметры user и pass в теле запроса. На стороне сервера ожидаются параметры с именами "user" и "pass", но вместо этого сервер получает их как "REQUEST_ARGS.user" и "REQUEST_ARGS.pass". Это явный признак того, что сервер не может правильно распарсить тело запроса, то есть формат, в котором мы передали form-data, оказался некорректным.

Исходный код запроса может выглядеть так:


//Попытка
//Создаем HTTPS соединение
АдресСервера = "api2.krs.ru";
РесурсНаСервере = "/api/rest/specific";
HTTPСоединение = Новый HTTPСоединение(АдресСервера,,,,,,Новый ЗащищенноеСоединениеOpenSSL);
//...
Если НЕ HTTPСоединение = Неопределено Тогда
    //Параметры Post запроса
    мПараметры = Новый Соответствие;
    мПараметры.Вставить("user" , "admin"); //Логин
    мПараметры.Вставить("pass" , "12345"); //Пароль
    мПараметры.Вставить("date_from", "20250611"); //С даты (YYYYMMDD)
    мПараметры.Вставить("date_to" , "20250615"); //По дату (YYYYMMDD)

    // Преобразуем параметры в строку form-data
    Разделитель = СтрЗаменить(Новый УникальныйИдентификатор, "-", ""); // Ошибка тут!
    ТипКонтента = СтрШаблон("multipart/form-data; boundary=%1", Разделитель);
    ПараметрыСтрокой = "";
    Для Каждого Параметр Из мПараметры Цикл
        ПараметрыСтрокой = ПараметрыСтрокой + Разделитель + Параметр.Ключ + "=" + Параметр.Значение;
    КонецЦикла;

    HTTPЗапрос = Новый HTTPЗапрос;
    HTTPЗапрос.АдресРесурса = РесурсНаСервере;
    HTTPЗапрос.Заголовки.Вставить("Content-Type", ТипКонтента);
    HTTPЗапрос.УстановитьТелоИзСтроки(ПараметрыСтрокой, "UTF-8"); // Устанавливаем тело запроса
    HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
    //...
КонецЕсли;

В этом примере есть несколько критических ошибок, которые приводят к неверному формированию multipart/form-data. Основные из них:

  1. Неправильный формат multipart/form-data: Для этого типа контента требуется строгий формат с разделителями (boundary), заголовками для каждой части (Content-Disposition) и пустыми строками. Простое склеивание параметров в строку с разделителем недостаточно.
  2. Передача тела строкой: Для multipart/form-data, особенно если планируется отправлять файлы или бинарные данные, крайне желательно формировать тело запроса как двоичные данные. Метод УстановитьТелоИзСтроки() может быть неподходящим или менее гибким.

Инструменты для отладки HTTP-запросов

Прежде чем погружаться в код 1С (поможет пошаговая отладка и вычисление выражений в 1С), рассмотрим незаменимые инструменты, которые помогут нам в отладке любых HTTP-запросов:

1. Postman

Postman — это мощный инструмент для тестирования, разработки и документирования API. Он позволяет легко формировать HTTP-запросы любой сложности, включая form-data, отправлять их и анализировать ответы. Если ваш запрос успешно работает в Postman, это означает, что проблема не в API сервера, а в том, как 1С формирует запрос.

  1. Создание запроса: В Postman вы можете выбрать тип запроса (POST, GET и т.д.), указать URL, а затем на вкладке "Body" выбрать "form-data" и добавить необходимые пары ключ-значение.
  2. Генерация кода: Самая полезная функция для нас — это "Code snippet". Postman может сгенерировать пример кода для различных языков программирования, включая cURL. Анализируя этот cURL-запрос, вы сможете понять, какие заголовки и какое тело должны быть отправлены.
  3. Проверка URL: Убедитесь, что URL в Postman полностью совпадает с тем, что вы используете в 1С. Даже маленькая опечатка или лишний символ могут привести к ошибке 403.

2. Fiddler (или Charles Proxy)

Fiddler — это HTTP/HTTPS прокси-сервер, который перехватывает весь веб-трафик с вашего компьютера. Он позволяет увидеть, какие именно байты отправляет 1С на сервер, включая все заголовки и тело запроса. Это критически важно, чтобы сравнить "рабочий" запрос из Postman с "нерабочим" запросом из 1С.

  1. Установка и настройка: После установки Fiddler необходимо убедиться, что он настроен на перехват HTTPS-трафика.
  2. Перезапуск 1С: Чтобы 1С начала использовать Fiddler в качестве прокси, может потребоваться перезапустить клиент 1С после запуска Fiddler.
  3. Анализ запросов: В интерфейсе Fiddler вы увидите список всех HTTP/HTTPS запросов. Выберите нужный запрос из 1С, и на вкладках "Inspectors" сможете просмотреть его заголовки ("Headers") и тело ("Body") в различных форматах.

Правильное формирование form-data в 1С

Для корректной отправки multipart/form-data в 1С нам необходимо вручную сформировать тело запроса, используя объекты ПотокВПамяти и ЗаписьДанных. Это позволит нам точно контролировать структуру данных, разделители (boundary) и кодировку.

1. Принципы multipart/form-data

Формат multipart/form-data предполагает разделение каждого параметра или файла на отдельные части с помощью специального разделителя (boundary). Каждая часть начинается с разделителя, затем содержит заголовки (например, Content-Disposition, name, filename, Content-Type), за которыми следуют пустая строка и само значение параметра или содержимое файла. В конце всего тела запроса добавляется финальный разделитель.

Важные моменты:

  1. Каждый разделитель должен начинаться с двух дефисов (--).
  2. После заголовков каждой части обязательно должна быть пустая строка.
  3. Последний разделитель должен заканчиваться двумя дефисами (--).

2. Пример кода для формирования тела запроса form-data

Рассмотрим процедуру, которая инкапсулирует логику формирования тела multipart/form-data. Эта процедура будет принимать объект HTTPЗапрос и набор параметров, а затем устанавливать тело запроса и необходимые заголовки.

Для создания динамического разделителя (boundary) мы будем использовать Новый УникальныйИдентификатор, удаляя дефисы. Этот разделитель должен быть уникальным, чтобы не встретиться в самих данных.


Процедура ВставитьПараметрыВЗапрос(АдресСервера, ЗапросHTTP, UserId, api_key, date_from, date_to)
    Разделитель = СтрЗаменить(Новый УникальныйИдентификатор, "-", "");
    ПотокВПамяти = Новый ПотокВПамяти;
    ЗаписьДанных = Новый ЗаписьДанных(ПотокВПамяти); // По умолчанию UTF-8

    // Добавляем параметр "user"
    ЗаписьДанных.ЗаписатьСтроку("--"+Разделитель);
    ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""user""");
    ЗаписьДанных.ЗаписатьСтроку(""); // Обязательная пустая строка
    ЗаписьДанных.ЗаписатьСтроку(СокрЛП(UserId));

    // Добавляем параметр "pass" (или api_key)
    ЗаписьДанных.ЗаписатьСтроку("--"+Разделитель);
    ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""pass"""); // Или "api_key" в зависимости от API
    ЗаписьДанных.ЗаписатьСтроку(""); // Обязательная пустая строка
    ЗаписьДанных.ЗаписатьСтроку(СокрЛП(api_key));

    // Добавляем параметр "date_from"
    ЗаписьДанных.ЗаписатьСтроку("--"+Разделитель);
    ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""date_from""");
    ЗаписьДанных.ЗаписатьСтроку(""); // Обязательная пустая строка
    ЗаписьДанных.ЗаписатьСтроку(СокрЛП(date_from));

    // Добавляем параметр "date_to"
    ЗаписьДанных.ЗаписатьСтроку("--"+Разделитель);
    ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""date_to""");
    ЗаписьДанных.ЗаписатьСтроку(""); // Обязательная пустая строка
    ЗаписьДанных.ЗаписатьСтроку(СокрЛП(date_to));

    // Завершающий разделитель
    ЗаписьДанных.ЗаписатьСтроку("--"+Разделитель+"--");
    ЗаписьДанных.Закрыть();

    // Получаем тело запроса в виде двоичных данных
    ДвоичныеДанныеТело = ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные;
    ЗапросHTTP.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеТело);

    // Устанавливаем заголовки
    ТипКонтента = СтрШаблон("multipart/form-data; boundary=%1", Разделитель);
    ЗапросHTTP.Заголовки.Вставить("Content-Type" , ТипКонтента);
КонецПроцедуры

Разберем по шагам ключевые моменты этой процедуры:

  1. Уникальный разделитель: Создаем уникальную строку, которая будет использоваться в качестве разделителя.
  2. ПотокВПамяти: Мы используем этот объект для последовательной записи данных в память, что крайне эффективно для формирования сложных тел запросов.
  3. Заголовки частей: Указываем Content-Disposition: form-data и имя поля. Это стандарт, который понимают все современные API.
  4. Пустая строка: Критически важный элемент перед записью самого значения параметра.

3. Итоговый код формирования и отправки запроса

Теперь соберем все вместе. Данный подход часто применяется в специализированных интеграциях, таких как получение данных о контрагенте по API, где точность формирования запроса определяет успех операции.


// ... код подготовки данных ...
Если НЕ HTTPСоединение = Неопределено Тогда
    Пока Выборка.Следующий Цикл
        // ... подготовка UserId и api_key ...
        Попытка
            HTTPЗапрос = Новый HTTPЗапрос(РесурсНаСервере);
            ВставитьПараметрыВЗапрос(АдресСервера, HTTPЗапрос, UserId, api_key, мСДаты, мПоДату);
            HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
            // ... обработка ответа ...
        Исключение
            // ... обработка ошибок ...
        КонецПопытки;
    КонецЦикла;
КонецЕсли;

Альтернативный способ: application/x-www-form-urlencoded

Если API не требует передачи файлов и принимает простые параметры ключ-значение, то более простым вариантом может быть использование Content-Type: application/x-www-form-urlencoded. Этот способ значительно проще в реализации, но не всегда подходит, особенно если API строго ожидает multipart/form-data.

Пример такого запроса:


//...
ПараметрыСтрокой = "";
Для Каждого Параметр Из мПараметры Цикл
    Если НЕ ПустаяСтрока(ПараметрыСтрокой) Тогда
        ПараметрыСтрокой = ПараметрыСтрокой + "&";
    КонецЕсли;
    ПараметрыСтрокой = ПараметрыСтрокой + Параметр.Ключ + "=" + КодироватьСтроку(Параметр.Значение, "UTF-8", "URL");
КонецЦикла;

HTTPЗапрос = Новый HTTPЗапрос(РесурсНаСервере);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
HTTPЗапрос.УстановитьТелоИзСтроки(ПараметрыСтрокой, "UTF-8");
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
//...

Важные аспекты и советы

  1. Аккуратность до запятой: Любая опечатка или отсутствие пустой строки может привести к тому, что сервер не сможет распарсить запрос.
  2. Кодировка: В ЗаписьДанных по умолчанию используется UTF-8, что является хорошим стандартом.
  3. Ошибки 401 и 403:
    • Ошибка 401 Unauthorized обычно означает проблемы с аутентификацией. Например, при получении токена авторизации RuStore требуется использование подписи RSA с хешированием SHA512.
    • Ошибка 403 Forbidden часто указывает на некорректно сформированные параметры запроса, как мы разобрали выше — поможет интеграция 1С с Telegram через REST API.
  4. Сложные интеграции: Для реализации надежных обменов, таких как интеграция 1С с Google Таблицами через сервисный аккаунт, всегда следуйте спецификации API в части формирования заголовков и тела запроса.

Для ускорения разработки и минимизации ошибок при создании собственных интерфейсов можно использовать универсальный модуль с процедурами для быстрой разработки HTTP-сервисов.

Следуя этим рекомендациям и используя инструменты отладки, вы сможете эффективно решить проблемы с отправкой HTTPS REST запросов с параметрами form-data из 1С и успешно интегрировать вашу систему с внешними сервисами.

← На главную