При разработке интеграций между 1С и внешними веб-сервисами (CRM-системами, сайтами, платежными шлюзами) разработчики часто сталкиваются с ошибками протокола HTTP. Одна из наиболее коварных и неоднозначных ошибок — 422 Unprocessable Entity (Необрабатываемый экземпляр). Эта ошибка означает, что сервер понял запрос и синтаксис данных корректен, но сами данные содержат логические ошибки или не соответствуют ожиданиям сервера.
В этой статье мы подробно разберем, почему возникает ошибка 422 при отправке POST-запросов из среды 1С, рассмотрим решение основной проблемы автора темы, а также изучим скрытые подводные камни платформы 1С, которые могут приводить к подобным сбоям даже при визуально «идеальном» JSON-пакете.
Давайте проанализируем исходный код, предоставленный автором темы. Автор формирует JSON-тело запроса, устанавливает его в объект запроса и выполняет отправку на сервер. Однако сервер возвращает ошибку 422, заявляя, что обязательные параметры (код, фамилия, email) не были переданы, хотя в теле запроса они присутствуют.
Причина кроется в заголовках HTTP-запроса. В коде автора заданы следующие заголовки:
Заголовки = Новый Соответствие;
Заголовки.Вставить("Connection", "keep-alive");
Заголовки.Вставить("Accept", "application/json");
Здесь допущена критическая ошибка: указан заголовок Accept, который сообщает серверу, какой тип ответа ожидает получить 1С, но полностью отсутствует заголовок Content-Type. Без этого заголовка веб-сервер не знает, в каком формате пришли входящие данные в теле POST-запроса. Он может интерпретировать их как обычный текст, форму application/x-www-form-urlencoded или вовсе проигнорировать тело, посчитав его пустым. В результате валидатор на стороне сервера сообщает, что обязательные поля не заполнены, и возвращает статус 422.
Чтобы исправить эту проблему, необходимо обязательно добавить заголовок Content-Type со значением application/json. Посмотрим на правильный вариант заполнения заголовков:
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json");
Заголовки.Вставить("Accept", "application/json");
После замены или добавления этого заголовка сервер сможет корректно распознать входящий поток байтов как JSON-структуру, успешно распарсить её и сопоставить переданные свойства с ожидаемыми параметрами.
Даже после настройки заголовка Content-Type вы можете продолжить получать ошибку 422 или 400 (Bad Request). Это связано с поведением платформы 1С при сериализации строк в двоичные данные.
Выясним причину: метод HTTPЗапрос.УстановитьТелоИзСтроки() по умолчанию добавляет в начало отправляемой строки невидимый символ — BOM (Byte Order Mark). Для текстовых файлов Windows это стандартное поведение, но для современных веб-серверов (особенно написанных на PHP, Python, Go или Node.js) наличие BOM перед JSON-строкой является критической ошибкой синтаксиса. Парсер сервера спотыкается о невидимые байты в самом начале потока и выдает ошибку парсинга, либо считает тело запроса невалидным.
Рассмотрим, как правильно решить эту проблему в современных версиях платформы 1С v8.3. Нам необходимо явно указать кодировку и отключить использование BOM в третьем параметре метода:
// Правильная установка тела запроса без BOM
Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Если вы работаете на старых версиях платформы (например, ранних релизах 8.3 или 8.2), где третий параметр метода недоступен, можно прибегнуть к хитрости и указать кодировку "CESU-8". Она аналогична UTF-8, но принудительно исключает запись BOM:
// Обходной путь для старых платформ
Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, "CESU-8");
Для обеспечения максимальной совместимости с внешними API рекомендуется не просто указывать тип контента, но и явно прописывать кодировку в самом заголовке Content-Type. Это исключит двусмысленность на стороне веб-сервера:
Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Поскольку ошибка 422 напрямую связана с семантикой данных, веб-сервер может отвергнуть запрос, если типы переданных полей не соответствуют внутренней схеме API. Проанализируем закомментированный код автора:
// "birthday": null,
// "employment": "02.04.2001"
Здесь кроются две потенциальные проблемы:
"02.04.2001" часто является причиной падения валидации на стороне сервера. Подавляющее большинство современных REST API ожидают дату в международном стандарте ISO 8601 (например, "2001-04-02" или "2001-04-02T00:00:00Z"). Если передать дату в локальном российском формате, сервер не сможет ее распознать и вернет ошибку 422. При сериализации объектов 1С в JSON рекомендуется использовать системные параметры записи для автоматического преобразования дат:
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(,,, ФорматДатыJSON.ISO, ВариантЗаписиДатыJSON.ЛокальнаяДата);
ЗаписатьJSON(ЗаписьJSON, JSONзапрос, ПараметрыЗаписи);
Null и Неопределено. При сериализации в JSON значение Null преобразуется в стандартный null, а Неопределено может вообще опускаться или вызывать ошибки в зависимости от настроек сериализатора. Если внешнее API строго требует передачу пустого значения для необязательного поля (например, "birthday": null), убедитесь, что в структуре 1С этому свойству присвоено именно системное значение Null.В коде автора для подготовки JSON-данных используется объект типа Соответствие:
JSONзапрос = Новый Соответствие;
JSONзапрос.Вставить("code", "4066");
В экосистеме 1С для составления JSON-документов гораздо удобнее и предпочтительнее использовать объект Структура. Запись структуры происходит быстрее, а сам код выглядит лаконичнее и понятнее для других разработчиков. Использовать Соответствие имеет смысл только тогда, когда ключи JSON-объекта содержат спецсимволы, недопустимые в именах свойств структуры 1С (например, дефисы "client-id", точки или пробелы).
Посмотрим на пример использования структуры:
JSONзапрос = Новый Структура;
JSONзапрос.Вставить("code", "4066");
JSONзапрос.Вставить("first_name", "Стас");
JSONзапрос.Вставить("email", "test1231@test.ru");
Обратим внимание на следующие строки в коде автора перед отправкой запроса:
Запрос.ПолучитьТелоКакСтроку();
Запрос.ПолучитьТелоКакПоток();
Результат = Соединение.ОтправитьДляОбработки(Запрос);
Эти вызовы абсолютно бесполезны. Метод ПолучитьТелоКакСтроку() вызывается «в пустоту», его результат никуда не присваивается. Более того, повторное чтение тела запроса в память как строки и как потока перед отправкой несет накладные расходы и на некоторых версиях платформы может приводить к ошибкам блокировки потока данных. Их необходимо полностью удалить из кода.
Иногда разработчики путают передачу параметров в теле запроса (Request Body) и в строке URL (Query Parameters). Если спецификация к вашему API указывает, что метод POST ожидает параметры в строке запроса, то отправка их внутри JSON-тела также приведет к ошибке 422. В этом случае адрес ресурса должен формироваться динамически:
АдресРесурса = "api/manager?code=4066&email=test1231@test.ru";
Запрос = Новый HTTPЗапрос(АдресРесурса, Заголовки);
// Тело запроса при этом может оставаться пустым
Объединим все наши рекомендации и исправления в единый надежный шаблон кода для отправки POST-запросов в формате JSON из 1С без риска получить ошибку 422:
// 1. Создаем защищенное соединение (для https)
ssl = Новый ЗащищенноеСоединениеOpenSSL;
// 2. Инициализируем HTTP-соединение
Соединение = Новый HTTPСоединение(
"api.mysite.ru", // Хост
443, // Порт
"логин", // Пользователь
"пароль", // Пароль
, // Прокси
15, // Таймаут в секундах
ssl // Защищенное соединение
);
// 3. Формируем заголовки запроса
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Заголовки.Вставить("Accept", "application/json");
// 4. Подготавливаем данные в структуре 1С
ДанныеЗапроса = Новый Структура;
ДанныеЗапроса.Вставить("code", "4066");
ДанныеЗапроса.Вставить("first_name", "Стас");
ДанныеЗапроса.Вставить("last_name", "Мельников");
ДанныеЗапроса.Вставить("email", "test1231@test.ru");
// Для корректной передачи пустого значения используем Null
ДанныеЗапроса.Вставить("birthday", Null);
// 5. Сериализуем структуру в строку JSON с настройками дат ISO 8601
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(,,, ФорматДатыJSON.ISO, ВариантЗаписиДатыJSON.ЛокальнаяДата);
ЗаписатьJSON(ЗаписьJSON, ДанныеЗапроса, ПараметрыЗаписи);
ТелоЗапроса = ЗаписьJSON.Закрыть();
// 6. Создаем HTTP-запрос и устанавливаем тело БЕЗ BOM маркера
Запрос = Новый HTTPЗапрос("api/manager", Заголовки);
Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Попытка
// 7. Отправляем запрос на сервер
Результат = Соединение.ОтправитьДляОбработки(Запрос);
Сообщить("Код состояния ответа: " + Результат.КодСостояния);
// 8. Читаем ответ сервера
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(Результат.ПолучитьТелоКакСтроку());
ОтветСервера = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Если Результат.КодСостояния = 200 Или Результат.КодСостояния = 201 Тогда
Сообщить("Данные успешно обработаны сервером!");
Иначе
Сообщить("Произошла ошибка обработки. Ответ сервера: " + Результат.ПолучитьТелоКакСтроку());
EndЕсли;
Исключение
Сообщить("Ошибка при выполнении HTTP-запроса: " + ОписаниеОшибки());
КонецПопытки;
Используя данный шаблон, вы гарантируете корректную передачу заголовков, отсутствие паразитных байтов в начале пакета, правильную кодировку символов и валидный формат дат, что позволит полностью избежать появления ошибок серии 4xx при интеграции с внешними системами.