При интеграции 1С со сторонними веб-сервисами (особенно написанными на стеке .NET) разработчики часто сталкиваются с ситуацией, когда корректно сформированный POST-запрос с телом в формате JSON отклоняется сервером. В ответ возвращается статус 400 (Bad Request) и сообщение об ошибке валидации.
Рассмотрим типичную ситуацию. Изначально стоит задача переписать следующий curl-запрос на язык 1С:
curl -X POST "https://api-сервер/api/Search/SearchCompany?apiVersion=1.3" \
-H "accept: text/plain" \
-H "accessKey: ВАШ_КЛЮЧ" \
-H "Content-Type: application/json-patch+json" \
-d "{ \"language\": \"Russian\", \"searchCompanyParameters\": { \"taxNumber\": \"261220411395\" } }"
Разберем базовый вариант кода в 1С, который часто пишут разработчики для решения этой задачи:
Процедура ОтправитьHTTPЗапрос()
СтрокаURI = "api-сервер/api/Search/SearchCompany?apiVersion=1.3";
СтруктураURI = СтруктураURI(СтрокаURI); // Функция разбора URL
HTTPСоединение = Новый HTTPСоединение(СтруктураURI.Хост, СтруктураURI.Порт,,,,,Новый ЗащищенноеСоединениеOpenSSL);
Заголовки = Новый Соответствие;
Заголовки.Вставить("accept","text/plain");
Заголовки.Вставить("accessKey","ВАШ_КЛЮЧ");
Заголовки.Вставить("Content-Type","application/json-patch+json");
СтруктураДляJSON = Новый Структура("language","Russian");
СтруктураДляJSON.Вставить("searchCompanyParameters", Новый Структура("taxNumber","261220411395"));
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, СтруктураДляJSON);
ТелоЗапроса = Запись.Закрыть();
HTTPЗапрос = Новый HTTPЗапрос(СтруктураURI.ПутьНаСервере, Заголовки);
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапроса);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
СтрокаОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
КонецПроцедуры
При выполнении этого кода сервер возвращает ошибку:
{"errors":{"":["Unexpected character encountered while parsing value: . Path '', line 0, position 0."],"searchRequest":["The searchRequest field is required."]},"type":" validation errors occurred.","status":400}
Проанализируем эту ошибку. Перевод гласит: "При синтаксическом анализе значения обнаружен неожиданный символ". Если скопировать тело JSON и проверить его в любом онлайн-валидаторе, он покажет, что JSON абсолютно корректен. Так откуда берется ошибка? Выясним истинную причину.
Символ, который в тексте ошибки выглядит как пустота или странная точка перед словом Path, — это BOM (Byte Order Mark, маркер последовательности байтов). В Юникоде он обозначается как \uFEFF.
Многие современные веб-сервисы строго следуют стандарту RFC 8259. Согласно этому стандарту, поток данных JSON не должен начинаться с маркера BOM. Если строгий парсер (например, Newtonsoft.Json) видит эти служебные байты в самом начале запроса, он спотыкается на нулевой позиции, так как ожидает увидеть открывающую фигурную скобку {, а не служебный символ.
Почему это происходит в 1С? Посмотрим на метод УстановитьТелоИзСтроки(). Если использовать его с параметрами по умолчанию:
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапроса);
Платформа 1С (в зависимости от режима совместимости, обычно 8.3.5 и ниже) при формировании строки в кодировке UTF-8 принудительно добавляет BOM-маркер в начало строки. На некоторых компьютерах или при использовании корпоративных прокси-серверов этот символ может "срезаться" сетевым оборудованием (поэтому иногда код магическим образом начинает работать на другой машине), но полагаться на это нельзя.
Чтобы код гарантированно работал на любой машине и при любых сетевых настройках, нам необходимо явно запретить платформе 1С добавлять маркер BOM при отправке запроса. Разберем по шагам, как это сделать.
Метод УстановитьТелоИзСтроки принимает дополнительные параметры кодировки и использования BOM. Запишем вызов метода правильно:
HTTPЗапрос = Новый HTTPЗапрос(СтруктураURI.ПутьНаСервере, Заголовки);
// Явно отключаем BOM третьим параметром:
HTTPЗапрос.УстановитьТелоИзСтроки(
ТелоЗапроса,
КодировкаТекста.UTF8,
ИспользованиеByteOrderMark.НеИспользовать
);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Передав ИспользованиеByteOrderMark.НеИспользовать, мы гарантируем, что тело запроса начнется ровно со скобки {, и принимающий сервер успешно распарсит наш JSON.
Если вы работаете на устаревшей версии платформы 1С или находитесь в жестком режиме совместимости, где у метода УстановитьТелоИзСтроки недоступен параметр отключения BOM, мы можем обойти эту проблему, сформировав тело запроса через временный файл.
Рассмотрим этот алгоритм подробнее:
ЗаписьТекста, явно указав отсутствие BOM.ДвоичныеДанные.Посмотрим на пример реализации:
ИмяВремФайла = ПолучитьИмяВременногоФайла("json");
// 5-й параметр в Ложь отключает добавление BOM при записи файла
ЗаписьТ = Новый ЗаписьТекста(ИмяВремФайла, КодировкаТекста.UTF8, , , Ложь);
ЗаписьТ.Записать(ТелоЗапроса);
ЗаписьТ.Закрыть();
// Читаем чистые байты без спецсимволов
ДвоичныеДанныеТела = Новый ДвоичныеДанные(ИмяВремФайла);
HTTPЗапрос = Новый HTTPЗапрос(СтруктураURI.ПутьНаСервере, Заголовки);
// Устанавливаем тело запроса из двоичных данных
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеТела);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
// Не забываем удалить временный файл
УдалитьФайлы(ИмяВремФайла);
В качестве еще одного "хака" для старых платформ можно рассмотреть передачу другой кодировки. Если вместо стандартного UTF-8 указать кодировку CESU-8, платформа 1С сформирует корректный текстовый поток без добавления маркера BOM.
HTTPЗапрос.УстановитьТелоИзСтроки(ТелоЗапроса, "CESU-8");
Однако этот способ стоит применять с осторожностью, так как кодировка CESU-8 по-иному обрабатывает некоторые специфические символы (суррогатные пары), выходящие за пределы базовой многоязычной плоскости Юникода. Если в ваших данных только стандартная кириллица и латиница, это сработает отлично.
Мы проанализировали ситуацию, когда казалось бы корректный код в 1С вызывает ошибки на принимающей стороне. Запомните главное правило: при работе с REST API и отправке JSON форматов из 1С, всегда явно контролируйте отсутствие Byte Order Mark (BOM). Использование конструкции ИспользованиеByteOrderMark.НеИспользовать избавит вас от долгих часов отладки "невидимых" символов и поиска проблем в сетевом оборудовании.