Разработка интеграций с внешними веб-сервисами по протоколу HTTP — одна из наиболее частых задач для современного программиста 1С. Однако на этом пути часто возникают неожиданные преграды. Типичная ситуация: один и тот же запрос в строке браузера или специализированной утилите (например, Postman) без проблем возвращает чистый JSON-пакет, но при попытке выполнить его из кода 1С платформа возвращает ответ со статусом 200 OK, тело которого представляет собой HTML-код страницы авторизации, ошибки или заглушки.
В этой статье мы подробно проанализируем причины такого поведения системы, разберем типичные ошибки при формировании запросов и научимся составлять правильный, отказоустойчивый код для взаимодействия с внешними API.
Рассмотрим первоначальный код, который часто пишут начинающие разработчики:
Соединение = Новый HTTPСоединение("myserver.com");
Заголовки = Новый Соответствие;
Заголовки.Вставить("date_from", "01.07.2019%2000:00:00");
Заголовки.Вставить("id", "12345");
Заголовки.Вставить("format", "json");
Запрос = Новый HTTPЗапрос("api/v1/getdata", Заголовки);
Результат = Соединение.Получить(Запрос);
Давайте выясним причину, почему этот код ошибочен. В данном примере параметры фильтрации (date_from, id, format) добавляются в объект Соответствие, который передается в качестве второго параметра конструктора HTTPЗапрос. Этот параметр отвечает исключительно за HTTP-заголовки (Headers) запроса.
HTTP-заголовки передают метаданные о запросе (например, тип контента, кодировку, токены авторизации). Сами же параметры запроса (Query Parameters) должны быть частью строки адреса (URI) и следовать после знака вопроса ?. Если сервер ожидает параметры в строке запроса, но не находит их там, он либо возвращает ошибку, либо игнорирует запрос и выдает стандартную стартовую страницу (HTML).
Предположим, мы исправили первую ошибку и перенесли параметры в строку запроса. Однако сервер все равно упорно возвращает HTML. Проанализируем типичный пример неверного конструирования пути:
// Ошибочный вариант: отсутствует слэш перед знаком вопроса
ПутьЗапроса = "api/v1/getdata?date_from=01.07.2019%2000:00:00&id=12345&format=json";
Запрос = Новый HTTPЗапрос(ПутьЗапроса);
Многие современные веб-серверы (Nginx, Apache, IIS) и бэкенд-фреймворки крайне чувствительны к наличию конечного слэша (trailing slash) перед параметрами запроса. Если ваш роутинг на сервере настроен строго на шаблон api/v1/getdata/, то запрос по пути api/v1/getdata?параметры (без слэша перед ?) может быть не распознан веб-сервером.
Что происходит в этом случае на стороне сервера? Вместо генерации ошибки 404 (Ресурс не найден), сервер может перенаправить некорректный запрос на единую точку входа (например, index.html в случае Single Page Application или CMS). В результате 1С получает код состояния 200, но в теле ответа будет лежать HTML-код главной страницы сайта.
Правильный вариант написания пути с учетом роутинга должен выглядеть так:
// Правильный вариант: слэш присутствует перед знаком вопроса
ПутьЗапроса = "api/v1/getdata/?date_from=01.07.2019%2000:00:00&id=12345&format=json";
Запрос = Новый HTTPЗапрос(ПутьЗапроса);
Пробелы, кириллические символы, знаки двоеточия и другие спецсимволы в строке GET-запроса должны быть обязательно закодированы в соответствии со стандартом RFC 3986. Передача «чистого» пробела или двоеточия напрямую в строке пути запроса в 1С может привести к непредсказуемому поведению сетевых библиотек операционной системы.
Для безопасного кодирования значений параметров в 1С предусмотрена встроенная функция КодироватьСтроку. Рассмотрим, как правильно формировать параметры запроса программно:
ПараметрДата = КодироватьСтроку("01.07.2019 00:00:00", СпособКодированияСтроки.КодировкаURL);
// На выходе получим безопасную строку: "01.07.2019%2000%3A00%3A00"
ПараметрИдентификатор = КодироватьСтроку("12345", СпособКодированияСтроки.КодировкаURL);
ПараметрФормат = КодироватьСтроку("json", СпособКодированияСтроки.КодировкаURL);
ПутьЗапроса = "api/v1/getdata/?"
+ "date_from=" + ПараметрДата
+ "&id=" + ПараметрИдентификатор
+ "&format=" + ПараметрФормат;
Запрос = Новый HTTPЗапрос(ПутьЗапроса);
Иногда веб-сервер умеет отдавать данные в разных форматах: HTML (для браузеров), XML или JSON (для интеграций). По умолчанию сервер пытается определить тип клиента по заголовку User-Agent. Так как 1С отправляет специфичный заголовок, сервер может посчитать её обычным браузером и вернуть красивую HTML-страничку вместо сырых данных.
Чтобы явно указать серверу, какой тип контента мы ожидаем получить в ответ, необходимо использовать заголовок согласования содержимого Accept. Обратите внимание, что заголовок Content-Type указывает серверу тип отправляемых нами данных, в то время как Accept — тип ожидаемых данных.
Добавим этот важный заголовок в наш запрос:
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/json");
Запрос = Новый HTTPЗапрос(ПутьЗапроса, Заголовки);
Если вы отправляете запрос на незащищенный адрес http://myserver.com, а сервер настроен на обязательное шифрование трафика, он автоматически перенаправит ваш запрос на https://myserver.com.
Современные браузеры делают этот переход (редирект со статусом 301 или 302) незаметно для пользователя. Однако объект HTTPСоединение в платформе 1С автоматически переходы не выполняет. Он честно вернет вам ответ сервера с кодом перенаправления и HTML-страницей, на которой будет написано что-то вроде "Document has moved".
Чтобы избежать этой проблемы, всегда используйте защищенное соединение https с инициализацией SSL-сертификатов:
SSLСоединение = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение("myserver.com", 443, , , , , SSLСоединение);
Давайте объединим все полученные знания в единую, готовую к использованию функцию. Мы автоматизируем сборку параметров, их кодирование, установку заголовков и обработку результата.
Функция ВыполнитьЗапросПолученияДанных() Экспорт
Сервер = "myserver.com";
Ресурс = "api/v1/getdata/"; // Обязательно закрываем слэшем перед параметрами
// Подготавливаем структуру параметров запроса
Параметры = Новый Структура;
Параметры.Вставить("date_from", "01.07.2019 00:00:00");
Параметры.Вставить("id", "12345");
Параметры.Вставить("format", "json");
// Собираем строку параметров с URL-кодированием
СтрокаПараметров = "";
Для Каждого ТекПараметр Из Параметры Цикл
ИмяБезПробелов = СокрЛП(ТекПараметр.Ключ);
ЗначениеКодированное = КодироватьСтроку(Строка(ТекПараметр.Значение), СпособКодированияСтроки.КодировкаURL);
Разделитель = ?(ЗначениеЗаполнено(СтрокаПараметров), "&", "");
СтрокаПараметров = СтрокаПараметров + Разделитель + ИмяБезПробелов + "=" + ЗначениеКодированное;
КонецЦикла;
ПолныйПутьРесурса = Ресурс + "?" + СтрокаПараметров;
// Задаем необходимые заголовки для получения JSON
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/json");
// Инициализируем защищенное HTTPS соединение
SSL = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение(Сервер, 443, , , , 15, SSL);
Запрос = Новый HTTPЗапрос(ПолныйПутьРесурса, Заголовки);
Попытка
Ответ = Соединение.Получить(Запрос);
КодСостояния = Ответ.КодСостояния;
Если КодСостояния = 200 Тогда
ДанныеJSON = Ответ.ПолучитьТелоКакСтроку("UTF-8");
Возврат ДанныеJSON;
Иначе
ВызватьИсключение "Сервер вернул код ошибки: " + КодСостояния;
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("ИнтеграцияHTTP", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат Неопределено;
КонецПопытки;
КонецФункции
Если код написан правильно, но сервер всё равно возвращает непредвиденный результат, необходимо посмотреть на отправляемый запрос «глазами сервера». Для этого используются специализированные локальные прокси-серверы (снифферы трафика). Самым простым и мощным инструментом является программа Fiddler Classic.
Рассмотрим пошагово, как перехватить запрос из 1С с помощью Fiddler:
8888.
// Указываем локальный прокси-сервер запущенного Fiddler
ПроксиСервер = Новый HTTPПрокси("127.0.0.1", 8888);
// Передаем прокси в конструктор HTTPСоединения
Соединение = Новый HTTPСоединение("myserver.com", , , , ПроксиСервер);
Теперь при выполнении кода в 1С все исходящие запросы отобразятся в левой панели программы Fiddler. Вы сможете детально изучить заголовки запроса, структуру URL, отправляемое тело, а также детальный ответ сервера, включая скрытые заголовки и сырые данные. Это позволяет мгновенно обнаружить лишние пробелы, отсутствующие слэши или неверные кодировки.