Как исправить ошибку 400 при отправке HTTP-запроса с телом x-www-form-urlencoded в 1С

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

В процессе интеграции 1С с внешними сервисами (например, платежными системами или API сторонних площадок) разработчики часто сталкиваются с ситуацией, когда запрос идеально отрабатывает в Postman, но возвращает ошибку 400 Bad Request при попытке реализации в коде 1С. Особенно часто это происходит при передаче данных в формате x-www-form-urlencoded. В данной статье мы подробно разберем, почему это происходит, проанализируем роль невидимых символов в заголовках и теле запроса, а также научимся правильно конфигурировать объекты HTTPСоединение и HTTPЗапрос.

Поиск причины: почему сервер отклоняет корректный запрос

Когда сервер возвращает код состояния 400, это означает «Плохой запрос». Сервер сообщает, что он не может понять запрос из-за неверного синтаксиса. Если параметры авторизации, адрес и заголовки кажутся верными, корень проблемы обычно кроется в теле запроса (Request Body). Рассмотрим ситуацию на примере интеграции с сервисом ws.lwo.by, который используется для работы с системой «Оплати».

Основная сложность заключается в том, как 1С работает с кодировками при использовании метода УстановитьТелоИзСтроки. По умолчанию при использовании кодировки UTF-8 платформа может добавлять в начало строки специальный префикс — BOM (Byte Order Mark). Это последовательность байтов (EF BB BF), которая указывает на порядок байтов в текстовом файле. Однако веб-серверы, ожидающие данные в формате application/x-www-form-urlencoded, воспринимают эти байты как мусор или некорректный символ в начале имени первого параметра. В результате сервер не может распарсить структуру ключ=значение и выдает ошибку 400.

Решение проблемы: правильное использование метода УстановитьТелоИзСтроки

Для того чтобы исключить передачу невидимого префикса BOM, нам необходимо явно указать платформе, что его использовать не нужно. Проанализируем синтаксис метода УстановитьТелоИзСтроки объекта HTTPЗапрос. Этот метод имеет три параметра:

  1. Строка (само тело запроса).
  2. Кодировка (по умолчанию UTF-8).
  3. Использование Byte Order Mark.

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


// Формируем тело запроса в формате ключ=значение
Боди = "grant_type=password&scope=openid&username=2022020202&password=111111";

ЗапросHTTP = Новый HTTPЗапрос("/token", ЗаголовокHTTP);

// Явно указываем кодировку и отключаем использование BOM
ЗапросHTTP.УстановитьТелоИзСтроки(Боди, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);

Именно использование системного перечисления ИспользованиеByteOrderMark.НеИспользовать во втором параметре (если считать от нуля) или в третьем (по счету) решает проблему в 90% случаев при работе с современными REST API.

Настройка HTTPСоединения: работа с портами и SSL

Еще один важный момент, который мы должны рассмотреть, — это правильное создание объекта HTTPСоединение. В строке адреса часто указывают порт, например ws.lwo.by:8243. Хотя в некоторых случаях 1С может корректно интерпретировать такую строку, правильным и надежным способом является разделение адреса сервера и номера порта.

Проанализируем параметры конструктора HTTPСоединение:

  1. Сервер: только доменное имя (например, "ws.lwo.by").
  2. Порт: числовое значение (например, 8243).
  3. Пользователь/Пароль: если требуется базовая авторизация на уровне соединения.
  4. Прокси: если работа идет через прокси-сервер.
  5. Таймаут: время ожидания ответа.
  6. ЗащищенноеСоединение: объект ЗащищенноеСоединениеOpenSSL для работы по протоколу HTTPS.

Посмотрим на пример правильной инициализации защищенного соединения с использованием специфического порта:


СерверИмя = "ws.lwo.by";
ПортСервера = 8243;
Защита = Новый ЗащищенноеСоединениеOpenSSL();

СоединениеHTTP = Новый HTTPСоединение(СерверИмя, ПортСервера, , , , , Защита);

Если вы используете порт 443 (стандарт для HTTPS) или 80 (для HTTP), его можно не указывать явно, 1С подставит его автоматически в зависимости от наличия параметра защищенного соединения. Однако для нестандартных портов (как 8243) явное указание в отдельном параметре критически важно.

Подготовка данных: кодирование параметров (URL Encoding)

При использовании типа контента application/x-www-form-urlencoded мы должны помнить, что все значения параметров должны быть закодированы. Если в вашем пароле или логине встречаются спецсимволы, такие как &, =, %, + или пробелы, они нарушат структуру запроса. Например, символ & сервер воспримет как разделитель нового параметра.

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


Функция СформироватьТелоЗапроса(Логин, Пароль)
    
    // Каждый параметр должен быть обработан функцией КодироватьСтроку
    СтрЛогин = КодироватьСтроку(Логин, СпособКодированияСтроки.КодированиеURL);
    СтрПароль = КодироватьСтроку(Пароль, СпособКодированияСтроки.КодированиеURL);
    
    Результат = "grant_type=password";
    Результат = Результат + "&scope=openid";
    Результат = Результат + "&username=" + СтрЛогин;
    Результат = Результат + "&password=" + СтрПароль;
    
    Возврат Результат;
    
КонецФункции

Использование СпособКодированияСтроки.КодированиеURL гарантирует, что все небезопасные символы будут заменены на их шестнадцатеричные представления (например, пробел станет %20 или +), что обеспечит корректную доставку данных до сервера.

Авторизация и заголовки

Для запроса токена часто требуется заголовок Authorization. Обычно это Basic авторизация, где передается строка в формате Base64(client_id:client_secret). При формировании этой строки важно убедиться, что в ней нет символов переноса строк, которые 1С может добавлять автоматически при кодировании длинных строк в Base64.

Проанализируем пример формирования заголовков:


ЗаголовокHTTP = Новый Соответствие;
ЗаголовокHTTP.Вставить("Content-Type", "application/x-www-form-urlencoded");

// Если требуется передать Accept для получения JSON
ЗаголовокHTTP.Вставить("Accept", "application/json");

// Пример формирования Basic авторизации (условно)
СтрокаАвторизации = "Basic " + Base64Строка(Новый ДвоичныеДанныеИзСтроки(Идентификатор + ":" + Секрет));
// Убираем возможные переносы строк, если строка длинная
СтрокаАвторизации = СтрЗаменить(СтрокаАвторизации, Символы.ПС, "");
СтрокаАвторизации = СтрЗаменить(СтрокаАвторизации, Символы.ВК, "");

ЗаголовокHTTP.Вставить("Authorization", СтрокаАвторизации);

Отладка запросов с помощью инструментов мониторинга

Если код по-прежнему возвращает ошибку, выясним причину с помощью внешних инструментов. Рассмотрим использование прокси-отладчика, такого как Fiddler или Charles. Эти программы позволяют перехватить запрос из 1С и сравнить его "байт-в-байт" с успешным запросом из Postman.

Для того чтобы 1С начала отправлять запросы через Fiddler, нужно настроить прокси-сервер в объекте HTTPСоединение. Обычно Fiddler слушает порт 8888 на локальном хосте (127.0.0.1). Проанализируем, как это сделать:


Прокси = Новый ИнтернетПрокси;
Прокси.Установить("https", "127.0.0.1", 8888);
Прокси.Установить("http", "127.0.0.1", 8888);

// Теперь соединение будет проходить через отладчик
СоединениеHTTP = Новый HTTPСоединение(СерверИмя, ПортСервера, , , Прокси, , Защита);

В Fiddler мы сможем увидеть вкладку Inspectors -> Raw. Именно там будет видно, добавила ли 1С невидимый символ BOM в начало тела или нет. Сравнение вкладки Raw для запроса из 1С и из Postman — самый быстрый способ найти лишние пробелы, неверные заголовки или скрытые символы.

Итоговый пример реализации

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


&НаСервере
Функция ПолучитьТокенДоступа()
    
    АдресСервера = "ws.lwo.by";
    Порт = 8243;
    ПутьКРесурсу = "/token";
    
    // 1. Настройка защищенного соединения
    Защита = Новый ЗащищенноеСоединениеOpenSSL();
    
    // 2. Формирование заголовков
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
    Заголовки.Вставить("Accept", "application/json");
    
    // Здесь должна быть ваша логика формирования Authorization
    // Заголовки.Вставить("Authorization", "Basic " + ЗакодированныеДанные);
    
    // 3. Формирование тела запроса
    Тело = "grant_type=password&scope=openid&username=2022020202&password=111111";
    
    // 4. Создание запроса и соединения
    Соединение = Новый HTTPСоединение(АдресСервера, Порт, , , , , Защита);
    Запрос = Новый HTTPЗапрос(ПутьКРесурсу, Заголовки);
    
    // КРИТИЧЕСКИ ВАЖНО: Отключаем BOM
    Запрос.УстановитьТелоИзСтроки(Тело, "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
    
    Попытка
        Ответ = Соединение.ОтправитьДляОбработки(Запрос);
        
        Если Ответ.КодСостояния = 200 Тогда
            // Успешная обработка JSON
            ЧтениеJSON = Новый ЧтениеJSON;
            ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
            РезультатСтруктура = ПрочитатьJSON(ЧтениеJSON);
            Возврат РезультатСтруктура;
        Иначе
            Сообщить("Ошибка сервера: " + Ответ.КодСостояния);
            Сообщить("Тело ответа: " + Ответ.ПолучитьТелоКакСтроку());
            Возврат Неопределено;
        КонецЕсли;
    Исключение
        Сообщить("Ошибка связи: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
    
КонецФункции

Соблюдение этих правил — отключение BOM, правильное URL-кодирование параметров и корректное разделение порта и адреса — позволит вам избежать большинства проблем при работе с внешними HTTP-сервисами из 1С.

← На главную