При интеграции 1С с внешними веб-сервисами разработчики часто сталкиваются с ситуацией, когда метод ОтправитьДляОбработки() или POST-запрос возвращает код состояния HTTP 400 Bad Request или 500 Internal Server Error. Проанализируем основные причины этой проблемы и разберем по шагам, как правильно формировать и отправлять XML-данные из 1С.
При создании XML-файла через объект ЗаписьXML и сохранении его на диск в начало файла часто добавляется маркер последовательности байтов (BOM). Большинство сторонних веб-серверов и REST API не ожидают наличие BOM перед стандартным объявлением <?xml version="1.0" encoding="UTF-8"?> и отклоняют весь запрос как невалидный с ошибкой 400.
Рассмотрим, как избежать использования BOM и исключить промежуточные временные файлы, сформировав XML непосредственно в оперативной памяти:
// Формируем XML в строковую переменную
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("scoreList");
// ... Заполняем структуру XML ...
Запись.ЗаписатьКонецЭлемента(); // scoreList
СтрокаXML = Запись.Закрыть();
// Передаем строку в запрос с явным указанием отключать BOM
HTTPЗапрос = Новый HTTPЗапрос(ОтносительныйURL);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаXML, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Выясним причину дублирования или искажения заголовков запроса. В платформе 1С при вызове методов УстановитьТелоИзСтроки() или УстановитьИмяФайлаТела() длина передаваемых данных рассчитывается автоматически, и заголовок Content-Length формируется самой платформой.
Если явно добавлять Content-Length в соответствие заголовков (особенно передавая числовое значение ДвоичныеДанные.Размер()), сервер получает продублированный или некорректно сформированный заголовок HTTP-пакета. Это неизбежно вызывает ошибку 400 Bad Request. Ручное добавление Content-Length выполнять не требуется.
Посмотрим на инициализацию объекта HTTPСоединение. Одной из самых распространенных ошибок является неправильное количество запятых при настройке параметров соединения.
Разберем параметры конструктора HTTPСоединение по порядку:
Сервер (Имя хоста)ПортПользовательПарольПроксиТаймаутЗащищенноеСоединение (Объект ЗащищенноеСоединениеOpenSSL)ИспользоватьАутентификациюОС (Булево)Если передать Истина в качестве 8-го параметра, платформа включит NTLM/Kerberos аутентификацию Windows, вместо создания защищенного HTTPS-соединения. Для работы по протоколу HTTPS нужно передавать объект защищенного соединения в 7-й параметр:
// Правильная инициализация HTTPS-соединения
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
HTTPСоединение = Новый HTTPСоединение("example.com", 443, , , , , ЗащищенноеСоединение);
Проанализируем формирование пути в объекте HTTPЗапрос. Относительный адрес (URL без указания домена) обязателен к передаче со начальным слэшем /. Если передать путь вида api/v1/import вместо /api/v1/import, веб-сервер воспринимает начальную строку HTTP-запроса как синтаксически неверную и возвращает код 400.
При обходе данных и формировании узлов XML использование простой функции Строка() для всех подряд значений приводит к нарушению XSD-схемы на стороне сервера:
1 500,50). Для XML следует форматировать числа без пробелов и с точкой в качестве разделителя дробей: Формат(Стр.План, "ЧГ=0; ЧРД=.").YYYY-MM-DDTHH:mm:ss).true или false вместо Истина и Ложь.При получении кодов состояния 400 или 500 веб-сервер почти всегда возвращает описание проблемы в теле ответа (например, информацию об отсутствующих обязательных полях XML). Проанализируем, как извлечь этот текст для отладки:
Результат = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если Результат.КодСостояния > 299 Тогда
ТекстОтвета = Результат.ПолучитьТелоКакСтроку();
Сообщить("Код состояния: " + Строка(Результат.КодСостояния));
Сообщить("Текст ответа сервера: " + ТекстОтвета);
КонецЕсли;
Считывание тела ответа позволит быстро выявить точные причины, по которым внешний сервис отвергает сформированный XML-документ.