При интеграции систем 1С:Предприятие со сторонними веб-сервисами и API разработчики часто сталкиваются с ошибками обработки HTTP-запросов. Одной из самых коварных является ошибка HTTP 422 (Unprocessable Entity), которая означает, что сервер понял запрос, но его содержимое не может быть обработано из-за синтаксических или логических ошибок. Часто в теле ответа возвращается пояснение: "JSON не должен быть пустым", хотя, казалось бы, данные в запрос были успешно переданы.
В этой статье мы подробно разберем, почему возникает такая ошибка при отправке POST-запросов из 1С, проанализируем основные причины конфликта заголовков и тела запроса, а также рассмотрим правильные шаблоны кода для успешной отправки данных.
Рассмотрим стандартную ситуацию, с которой сталкиваются разработчики. Код в 1С формирует JSON-строку, заполняет заголовки HTTP-запроса, указывает Content-Type: application/json и отправляет данные методом ОтправитьДляОбработки(). Однако сервер возвращает ошибку 422.
Давайте проанализируем типичный ошибочный код:
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-type", "application/json");
ЗапросPOST = Новый HTTPЗапрос("/v5/kontragets/update", Заголовки);
// ОШИБКА: попытка передать имя переменной "json=" внутри тела типа application/json
ЗапросPOST.УстановитьТелоИзСтроки("json=" + СтрокаJS, "CESU-8");
В данном фрагменте кроется фундаментальное противоречие, которое и приводит к ошибке 422:
Content-Type: application/json, мы сообщаем веб-серверу, что в теле запроса придет «чистый» JSON-документ (который должен начинаться со скобки { или [). Однако с помощью метода УстановитьТелоИзСтроки() в начало тела дописывается префикс json=. С точки зрения любого стандартного парсера JSON на стороне сервера, строка вида json={"key": "value"} является некорректной. Парсер не может ее прочитать, считает документ поврежденным или пустым и отклоняет запрос с ошибкой 422.0xEF, 0xBB, 0xBF. Многие современные веб-серверы (написанные на Node.js, Go, Python или PHP с включенным строгим парсингом) не умеют обрабатывать BOM в JSON-документах. Сервер спотыкается на этих невидимых байтах и считает JSON поврежденным. Использование устаревшей кодировки CESU-8 часто применяется как «костыль» для обхода этой проблемы, но в современных версиях 1С есть более изящное и правильное решение.В зависимости от требований внешнего API (принимает ли оно «чистый» JSON в теле или ожидает отправку классической веб-формы с параметрами), существует два корректных решения этой проблемы. Рассмотрим каждое из них подробно.
Если спецификация API требует передавать данные в переменной (например, в POST-параметре json), то веб-сервер ожидает стандартный формат веб-формы. Для этого необходимо изменить заголовок Content-Type, а саму JSON-строку обязательно закодировать для безопасной передачи в URL.
Разберем по шагам правильный алгоритм реализации этого способа:
ЗаписьJSON.КодироватьСтроку(), используя способ кодирования СпособКодированияСтроки.КодировкаURL. Это превратит спецсимволы, пробелы и кириллицу в безопасный для передачи HTTP-формат.Content-Type в значение application/x-www-form-urlencoded."json=" + КодированнаяСтрокаJSON.ИспользованиеByteOrderMark.НеИспользовать.Посмотрим на практический пример реализации этого решения в коде 1С:
// Шаг 1: Сериализация объекта в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);
ЗаписатьJSON(ЗаписьJSON, МассивОбъектов);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Шаг 2: URL-кодирование JSON-строки
КодированнаяСтрокаJSON = КодироватьСтроку(СтрокаJSON, СпособКодированияСтроки.КодировкаURL);
// Шаг 3: Настройка HTTP-запроса
АдресРесурса = "/v5/kontragets/update?token=авлолыводлфыовдв";
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
// Устанавливаем правильный заголовок для формы
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
// Шаг 4: Установка тела запроса без добавления BOM-байтов
HTTPЗапрос.УстановитьТелоИзСтроки("json=" + КодированнаяСтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
// Шаг 5: Отправка запроса
HTTPСоединение = Новый HTTPСоединение("api.server.com", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если HTTPОтвет.КодСостояния >= 200 И HTTPОтвет.КодСостояния < 300 Тогда
Сообщить("Данные успешно отправлены! Код: " + HTTPОтвет.КодСостояния);
Иначе
Сообщить("Ошибка выгрузки. Код состояния: " + HTTPОтвет.КодСостояния);
Сообщить("Ответ сервера: " + HTTPОтвет.ПолучитьТелоКакСтроку());
КонецЕсли;
Если стороннее API спроектировано по стандартам REST и ожидает непосредственно JSON-структуру в теле POST-запроса (без каких-либо оберток вроде json=), то использовать URL-кодирование и префиксы не нужно. Нам требуется передать сырую строку сериализованного JSON-объекта.
Проанализируем пример правильной отправки в формате application/json:
// Шаг 1: Сериализация в чистую JSON-строку
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);
ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Шаг 2: Создание запроса и заполнение заголовков
HTTPЗапрос = Новый HTTPЗапрос("/api/v1/update");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
// Шаг 3: Установка тела запроса (чистый JSON без BOM)
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
// Шаг 4: Отправка
HTTPСоединение = Новый HTTPСоединение("api.server.com", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Обратите внимание: в методе УстановитьТелоИзСтроки() мы передаем только саму переменную СтрокаJSON. Любые приставки перед ней нарушат валидность JSON-структуры.
Еще одна скрытая проблема, из-за которой сервер может возвращать ошибку 422 при валидации переданного JSON — это представление дробных чисел. В русской локализации платформы 1С:Предприятие при преобразовании чисел в строку в качестве разделителя дробной части используется запятая (например, 15,5 или 82,2).
Стандарт JSON строго требует, чтобы разделителем дробной части у чисел была **точка** (например, 15.5). Если вы вручную собираете JSON-строку или приводите типы через функцию Строка(), а затем передаете параметры в кавычках как текстовые строки, то принимающий сервер при попытке распарсить строку в числовой тип данных выдаст ошибку валидации.
Рекомендация: Всегда формируйте JSON с помощью специализированных объектов платформы: ЗаписьJSON и ЗаписатьJSON(). Передавайте числовые данные в сериализуемые структуры (такие как Структура, Массив или Соответствие) именно как числовой тип данных платформы 1С. В таком случае встроенный сериализатор автоматически преобразует их в стандартный JSON-формат с точкой-разделителем и без лишних кавычек.
Если же вам по какой-то причине необходимо передать число строкой, форматируйте его принудительно:
// Использование функции Формат для принудительной замены разделителя дробной части
ФорматированноеЧисло = Формат(Объект.Цена, "ЧГ=0; ЧРД=.");
Для предотвращения ошибок 422 при обмене данными между 1С и внешними API всегда проверяйте соответствие заголовка Content-Type реальному формату передаваемого тела запроса. Если вы шлете форму — кодируйте строку методом КодироватьСтроку, если чистый JSON — передавайте его без префиксов. И главное, никогда не забывайте отключать маркер BOM с помощью флага ИспользованиеByteOrderMark.НеИспользовать.