Работа с веб-сервисами и сторонними API через HTTP-запросы — одна из самых частых задач современного разработчика 1С. Однако при первой встрече с объектами HTTPСоединение и HTTPЗапрос легко совершить типовые ошибки, которые приводят к падению программы. Одной из таких классических ошибок является системное сообщение "Ошибка работы с Интернет: Couldn't resolve host name".
В этой статье мы подробно разберем, почему возникает эта ошибка, научимся правильно разделять адрес сервера и параметры запроса, разберемся с безопасным соединением HTTPS, настроим авторизацию и рассмотрим лучшие практики отправки файлов XML и JSON.
Давайте проанализируем код, с которого часто начинают новички:
// Ошибочный код
Запрос = "";
HTTPЗапрос = Новый HTTPЗапрос("http://iskander.mstarproject.com/?action=exchange&type=sale&mode=checkauth");
HTTPСоединение = Новый HTTPСоединение("http://iskander.mstarproject.com/?action=exchange&type=sale&mode=checkauth");
Ответ = HTTPСоединение.Получить(HTTPЗапрос);
При выполнении этого кода платформа выдает ошибку сопоставления имени хоста. Выясним причину этой проблемы. Объект HTTPСоединение предназначен для установления канала связи с сервером, а объект HTTPЗапрос — для описания конкретного обращения к ресурсу внутри этого канала.
Правило 1: В конструктор HTTPСоединение в качестве первого параметра (сервера) передается исключительно имя домена или IP-адрес. Указывать протокол (http:// или https://) перед именем хоста категорически нельзя! DNS-клиент платформы не сможет распознать такую строку и выдаст ошибку "Couldn't resolve host name".
Правило 2: Путь к конкретному ресурсу (скрипту, методу API) и GET-параметры (все, что идет после символа вопроса ?) должны передаваться в конструктор объекта HTTPЗапрос.
Рассмотрим, как правильно переписать этот фрагмент кода:
// Правильное разделение хоста и ресурса
АдресСервера = "iskander.mstarproject.com";
ПутьКРесурсу = "/?action=exchange&type=sale&mode=checkauth";
HTTPСоединение = Новый HTTPСоединение(АдресСервера);
HTTPЗапрос = Новый HTTPЗапрос(ПутьКРесурсу);
В современном веб-пространстве практически все ресурсы работают по защищенному протоколу HTTPS. Если вы просто укажете хост безопасного сайта в обычном HTTPСоединение, программа не сможет установить связь, так как по умолчанию 1С попытается подключиться по стандартному порту 80 (HTTP).
Для работы с HTTPS необходимо использовать объект ЗащищенноеСоединениеOpenSSL. Посмотрим на пример инициализации безопасного соединения:
АдресСервера = "iskander.mstarproject.com";
ПутьКРесурсу = "/?action=exchange&type=sale&mode=checkauth";
// Инициализируем защищенное соединение с использованием сертификатов операционной системы
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
// Передаем защищенное соединение в конструктор в качестве седьмого параметра
HTTPСоединение = Новый HTTPСоединение(АдресСервера, 443, , , , , ЗащищенноеСоединение);
HTTPЗапрос = Новый HTTPЗапрос(ПутьКРесурсу);
Использование порта 443 и объекта защиты гарантирует шифрование трафика и успешное прохождение рукопожатия (SSL handshake) с сервером.
Если веб-ресурс требует базовую авторизацию (Basic Authentication), учетные данные необходимо передавать в конструктор объекта HTTPСоединение. Обратите внимание на правильный порядок параметров:
Если перепутать порядок (например, сначала написать пароль, а потом логин), сервер вернет ошибку "401 Unauthorized" или "Access denied".
Логин = "MyUser";
Пароль = "MySecretPassword";
// Правильная инициализация с авторизацией
HTTPСоединение = Новый HTTPСоединение(АдресСервера, 443, Логин, Пароль, , , ЗащищенноеСоединение);
Часто разработчики совершают логическую ошибку, пытаясь отправить данные на сервер методом Получить(). На уровне протокола HTTP метод GET (который вызывается через Получить) не должен нести в себе тело запроса (payload). Некоторые веб-серверы принудительно очищают тело GET-запроса или блокируют его.
Если ваша задача — передать файл или строку данных (XML, JSON) на сервер, используйте другие методы:
ОтправитьДляОбработки(). Используется для создания новых записей или передачи больших объемов данных.Записать(). Обычно используется для обновления существующих объектов.Посмотрим, как меняется вызов в коде:
// Вместо: Ответ = HTTPСоединение.Получить(HTTPЗапрос);
// Используем для отправки данных:
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
На форумах часто советуют сомнительные архитектурные решения — например, оборачивать весь XML-файл внутрь JSON-строки. В реальной практике так делать не нужно. Рассмотрим два стандартных и максимально эффективных способа отправки XML на сервер.
Если XML-файл уже сформирован и сохранен на жестком диске, считывать его целиком в оперативную память сервера в виде строки нецелесообразно — это может вызвать перегрузку памяти при больших объемах выгрузки. В 1С предусмотрен метод УстановитьИмяФайлаТела(), который позволяет передать файл "потоком" напрямую с диска:
HTTPЗапрос = Новый HTTPЗапрос(ПутьКРесурсу);
// Указываем путь к локальному файлу XML
ПутьКФайлу = "C:\temp\export_data.xml";
HTTPЗапрос.УстановитьИмяФайлаТела(ПутьКФайлу);
// Обязательно сообщаем серверу тип передаваемого контента
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/xml; charset=utf-8");
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если вы генерируете XML динамически в коде с помощью объекта ЗаписьXML, записывать его на диск необязательно. Вы можете преобразовать его в строку и передать в тело запроса:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
// Пример формирования структуры XML
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("data");
ЗаписьXML.ЗаписатьТекст("Привет, веб-сервер!");
ЗаписьXML.ЗаписатьКонецЭлемента();
СтрокаXML = ЗаписьXML.Закрыть(); // Получаем готовую строку XML
HTTPЗапрос = Новый HTTPЗапрос(ПутьКРесурсу);
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаXML, КодировкаТекста.UTF8);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/xml; charset=utf-8");
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если ваше API требует передачи данных в формате JSON, то логика остается точно такой же, меняется только заголовок Content-Type и способ формирования строки:
// Формируем структуру данных
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("action", "exchange");
СтруктураДанных.Вставить("type", "sale");
// Сериализуем структуру в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
СтрокаJSON = ЗаписьJSON.Закрыть();
HTTPЗапрос = Новый HTTPЗапрос(ПутьКРесурсу);
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8);
// Указываем заголовок для JSON
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Никогда не забывайте добавлять заголовки к запросу. Без явного указания Content-Type принимающий веб-сервер не сможет понять, как обрабатывать полученный массив байтов — как текст, файл или структуру данных. Это может привести к ошибкам "400 Bad Request" или "500 Internal Server Error".
Чтобы ваше интеграционное решение было стабильным и отказоустойчивым, всегда анализируйте код состояния (status code), который возвращает сервер. Ошибки семейства 4xx (например, 404 — ресурс не найден) и 5xx (ошибка сервера) должны обрабатываться программно, чтобы не вызывать аварийного завершения работы у конечного пользователя.
Разберем пример комплексной обработки результата HTTP-запроса:
Попытка
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
КодCостояния = Ответ.КодСостояния;
Если КодCостояния >= 200 И КодCостояния < 300 Тогда
// Успешный запрос
ТекстОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Сообщить("Данные успешно отправлены! Ответ сервера: " + ТекстОтвета);
ИначеЕсли КодCостояния = 401 Или КодCостояния = 403 Тогда
Сообщить("Ошибка авторизации! Проверьте логин и пароль.");
ИначеЕсли КодCостояния = 404 Тогда
Сообщить("Запрашиваемый ресурс не найден на веб-сервере. Проверьте путь.");
Иначе
Сообщить("Сервер вернул ошибку с кодом: " + КодCостояния);
ТекстОшибки = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Сообщить("Детали ошибки: " + ТекстОшибки);
КонецЕсли;
Исключение
// Сюда программа попадет, если возникли сетевые проблемы (например, нет интернета)
ИнформацияОбОшибке = ИнформацияОбОшибке();
Сообщить("Произошла сетевая ошибка при подключении к серверу: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
КонецПопытки;
Используя предложенный шаблон, вы сможете гарантировать корректное поведение программы в любых внештатных ситуациях: от отсутствия интернета у клиента до ошибок на стороне внешнего API.