Интеграция 1С с внешними веб-сервисами, сайтами и API — одна из самых частых задач современного разработчика. Для обмена данными платформа использует встроенные механизмы работы с протоколами HTTP/HTTPS. Однако при написании кода легко совершить неочевидные ошибки, из-за которых сервер будет возвращать пустые ответы или классические ошибки маршрутизации, такие как 404 Not Found или 400 Bad Request.
В этой статье мы подробно разберем практический кейс с форума, выясним причины некорректной работы запросов и построим надежный, отказоустойчивый шаблон для работы с HTTP-запросами в 1С.
Рассмотрим ситуацию: у разработчика есть рабочий URL-адрес, который при вводе в адресную строку браузера возвращает корректный результат:
http://auto-motors.com.ua/1C/1C_status_item.php?signin=parol&bill[]=70171&status=9
Однако попытка реализовать этот запрос в коде 1С приводит к ошибке 404 Not Found (запрашиваемый ресурс не найден). Исходный нерабочий код выглядел следующим образом:
Сервер = "auto-motors.com.ua";
СТрокаЗапроса = "signin=parol&bill[]=70171&status=9";
Соединение = Новый HTTPСоединение(Сервер,,,,,60,Новый ЗащищенноеСоединениеOpenSSL(Новый СертификатКлиентаWindows));
Заголовки = Новый Соответствие();
Заголовки.Вставить("Content-Type", "text/html; charset=iso-8859-1");
Запрос = Новый HTTPЗапрос("/1C/", Заголовки);
Запрос.УстановитьТелоИзСтроки(СТрокаЗапроса);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку();
Сообщить(ТекстОтвета);
Проанализируем этот код и выделим несколько критических ошибок, которые привели к неработоспособности интеграции.
Разберем разницу между основными методами передачи данных. Когда мы вводим адрес в строку браузера, выполняется GET-запрос. Параметры этого запроса передаются непосредственно в строке URL после вопросительного знака ?.
В исходном коде разработчик использовал метод ОтправитьДляОбработки(), который отправляет POST-запрос. При этом параметры были помещены в тело запроса с помощью метода УстановитьТелоИзСтроки(), а путь запроса был сокращен до /1C/.
Веб-сервер, настроенный на прием GET-запроса по конкретному адресу скрипта, при обращении к папке /1C/ методом POST закономерно вернул ошибку 404, так как физически по этому пути обработчик отсутствует.
Решение: Для GET-запросов все параметры должны оставаться в строке адреса, а выполнять запрос нужно методом Получить() или общим методом ВызватьHTTPМетод("GET", Запрос).
Обратим внимание на инициализацию соединения:
Новый ЗащищенноеСоединениеOpenSSL(Новый СертификатКлиентаWindows)
Здесь допущены сразу две ошибки:
ЗащищенноеСоединениеOpenSSL принудительно заставляет 1С пытаться установить шифрованное соединение, что приводит к сбою подключения.Новый СертификатКлиентаWindows требуется исключительно для двусторонней (взаимной) аутентификации (mTLS), когда сервер требует от клиента персональный сертификат безопасности. Для обычных защищенных сайтов (HTTPS) достаточно передавать пустой конструктор Новый ЗащищенноеСоединениеOpenSSL().Решение: Если целевой ресурс доступен по обычному протоколу http://, параметр защищенного соединения в конструкторе HTTPСоединение указывать не нужно.
В платформе 1С адрес ресурса в конструкторе объекта HTTPЗапрос должен обязательно начинаться с косой черты — ведущего слэша /. Например, правильно писать "/1C/1C_status_item.php...", а не "1C/1C_status_item.php...". Хотя некоторые веб-серверы могут автоматически корректировать запросы, отсутствие слэша на строгих прокси-серверах или современных версиях Apache/Nginx гарантированно вызовет ошибку маршрутизации.
С учетом разбора ошибок, минимальный рабочий код для решения задачи автора выглядит следующим образом:
Сервер = "auto-motors.com.ua";
// Формируем полный путь к ресурсу вместе с параметрами запроса
ПутьКРесурсу = "/1C/1C_status_item.php?signin=parol&bill[]=70171&status=9";
// Создаем обычное HTTP-соединение без SSL
Соединение = Новый HTTPСоединение(Сервер);
// Создаем запрос с указанием полного пути
Запрос = Новый HTTPЗапрос(ПутьКРесурсу);
// Отправляем GET-запрос методом Получить()
Ответ = Соединение.Получить(Запрос);
// Выводим результат
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку();
Сообщить(ТекстОтвета);
Этот код лаконичен и решает поставленную задачу. Однако при интеграции с современными системами в реальных условиях эксплуатации этого может быть недостаточно. Рассмотрим лучшие практики создания надежных HTTP-запросов.
В строке запроса автора присутствуют квадратные скобки: bill[]. Символы [ и ] являются зарезервированными. Если веб-сервер работает под управлением современных версий Apache или Nginx с жесткими правилами безопасности, неэкранированные квадратные скобки в URL могут привести к ошибке сервера 400 Bad Request.
Для предотвращения подобных ситуаций все спецсимволы, кириллицу и пробелы в значениях параметров необходимо кодировать. В 1С для этого предназначена встроенная функция КодироватьСтроку. Символы [ и ] должны преобразовываться в %5B и %5D соответственно.
Рассмотрим пример правильного кодирования параметров:
ПараметрИмя = КодироватьСтроку("bill[]", СпособКодированияСтроки.КодировкаURL);
ПараметрЗначение = КодироватьСтроку("70171", СпособКодированияСтроки.КодировкаURL);
// В результате получим: bill%5B%5D=70171
Если адрес запроса хранится в базе в виде полной строки (например, в константе или справочнике) и может динамически меняться, вручную разделять его на адрес сервера и путь ресурса неудобно.
Если ваша конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), используйте готовую функцию разбора URL:
ПолныйАдрес = "https://auto-motors.com.ua/1C/1C_status_item.php?signin=parol&bill[]=70171&status=9";
СтруктураURI = ОбщегоНазначенияКлиентСервер.СтруктураURI(ПолныйАдрес);
// Получаем готовые параметры для создания соединения
Сервер = СтруктураURI.Хост;
Порт = СтруктураURI.Порт;
ПутьНаСервере = СтруктураURI.ПутьНаСервере; // Сюда автоматически войдут все параметры после "?"
Большинство современных сайтов используют защищенное соединение и автоматически перенаправляют пользователей с протокола http:// на https://.
Метод платформы 1С HTTPСоединение.Получить() по умолчанию не переходит по редиректам автоматически. Если сервер настроен на перенаправление, 1С вернет код состояния 301 Moved Permanently или 302 Found, а тело ответа будет пустым. Нам необходимо обрабатывать такие ситуации вручную, анализируя заголовки ответа.
Рассмотрим, как правильно написать функцию с поддержкой перенаправлений:
Функция ВыполнитьЗапросСРедиректом(Знач Сервер, Знач ПутьНаСервере, Знач ИспользоватьSSL = Ложь) Экспорт
ЗащищенноеСоединение = Неопределено;
Если ИспользоватьSSL Тогда
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
КонецЕсли;
Соединение = Новый HTTPСоединение(Сервер, , , , , 30, ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос(ПутьНаСервере);
Ответ = Соединение.Получить(Запрос);
// Проверяем статус редиректа
Если Ответ.КодСостояния = 301 Или Ответ.КодСостояния = 302 Тогда
// Получаем новый адрес из заголовка "Location"
НовыйАдрес = Ответ.Заголовки.Получить("Location");
Если ЗначениеЗаполнено(НовыйАдрес) Тогда
// Разбираем новый адрес с помощью БСП
СтруктураURI = ОбщегоНазначенияКлиентСервер.СтруктураURI(НовыйАдрес);
НовыйSSL = (СтруктураURI.Схема = "https");
// Повторяем запрос по новому адресу
Возврат ВыполнитьЗапросСРедиректом(СтруктураURI.Хост, СтруктураURI.ПутьНаСервере, НовыйSSL);
КонецЕсли;
КонецЕсли;
Возврат Ответ;
КонецФункции
Объединим все лучшие практики в один надежный метод, который выполняет безопасное кодирование параметров, автоматически определяет необходимость SSL и возвращает результат выполнения HTTP-запроса.
&НаСервере
Функция ОтправитьБезопасныйЗапросНаСервер()
// Исходные данные
БазовыйАдрес = "auto-motors.com.ua";
СкриптПуть = "/1C/1C_status_item.php";
// Безопасно формируем параметры запроса с URL-кодированием
Параметры = Новый Соответствие;
Параметры.Вставить("signin", "parol");
Параметры.Вставить("bill[]", "70171");
Параметры.Вставить("status", "9");
СтрокаПараметров = "";
Для Каждого Параметр Из Параметры Цикл
СимволРазделителя = ?(ПустаяСтрока(СтрокаПараметров), "", "&");
ИмяКод = КодироватьСтроку(Параметр.Ключ, СпособКодированияСтроки.КодировкаURL);
ЗначениеКод = КодироватьСтроку(Параметр.Значение, СпособКодированияСтроки.КодировкаURL);
СтрокаПараметров = СтрокаПараметров + СимволРазделителя + ИмяКод + "=" + ЗначениеКод;
КонецЦикла;
ПолныйПутьРесурса = СкриптПуть + "?" + СтрокаПараметров;
// Инициализируем соединение. Если сайт работает по HTTPS - включаем SSL
ИспользоватьHTTPS = Ложь; // Поменяйте на Истина, если целевой ресурс использует https://
ЗащищенноеСоединение = Неопределено;
Порт = 80;
Если ИспользоватьHTTPS Тогда
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
Порт = 443;
КонецЕсли;
Попытка
Соединение = Новый HTTPСоединение(БазовыйАдрес, Порт, , , , 30, ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос(ПолныйПутьРесурса);
// Устанавливаем базовые заголовки для корректной интерпретации сервером
Запрос.Заголовки.Вставить("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
Запрос.Заголовки.Вставить("Accept-Charset", "utf-8");
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Возврат ТекстОтвета;
Иначе
ВызватьИсключение "Сервер вернул ошибку с кодом состояния: " + Ответ.КодСостояния;
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("Интеграция.HTTPЗапросы", УровеньЖурналаРегистрации.Ошибка, , ,
ОписаниеОшибки());
ВызватьИсключение "Не удалось выполнить HTTP-запрос: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
КонецФункции
Использование этого шаблона защитит ваше решение от падений при изменении передаваемых данных, гарантирует корректную кодировку спецсимволов и позволит легко переключаться между протоколами HTTP и HTTPS.