При интеграции 1С с внешними веб-сервисами (например, сервисами поиска и заказа автозапчастей, логистическими агрегаторами или государственными порталами) разработчики часто сталкиваются с необходимостью авторизации. Большинство таких сервисов используют механизм Cookie для отслеживания сессии пользователя. Типичная проблема заключается в том, что первый запрос на авторизацию проходит успешно, но последующие запросы к методам сервиса возвращают ошибку доступа, так как сервер «не узнает» пользователя.
В этой статье мы подробно разберем, как правильно организовать работу с куками в 1С, используя COM-объект WinHttp.WinHttpRequest.5.1, и рассмотрим более современную альтернативу в виде встроенного объекта HTTPСоединение.
Разберем типичную ситуацию. Разработчик создает объект WinHttp, отправляет POST-запрос с логином и паролем, получает успешный ответ и заголовок Set-Cookie. Затем для следующего запроса создается новый экземпляр объекта. Это и есть главная ошибка. Согласно документации WinHTTP, объект самостоятельно управляет кэшем кук в рамках одной сессии. Как только вы уничтожаете объект в памяти и создаете новый, сессия обнуляется.
Проанализируем пример кода, который часто приводит к ошибке:
// Первый запрос - авторизация
WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.open("POST", АдресСервиса, 0);
WinHttp.send(ЗапросАвторизации);
Куки = WinHttp.getResponseHeader("Set-cookie");
// ВТОРОЙ ЗАПРОС - ОШИБКА
// Здесь создается новый объект, и старая сессия теряется!
WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.open("POST", АдресСервиса, 0);
WinHttp.SetRequestHeader("Cookie", Куки);
WinHttp.send(ЗапросДанных);
Для того чтобы WinHttp автоматически подставлял нужные куки в последующие запросы, нам достаточно не пересоздавать объект. Вызовем метод Open повторно для того же самого экземпляра. В этом случае объект сохранит внутреннее состояние сессии, полученное после авторизации.
Рассмотрим правильный алгоритм реализации:
WinHttp.WinHttpRequest.5.1.Open для следующего адреса или метода.Cookie, если сервер прислал Set-Cookie в первом ответе.Посмотрим на исправленный код:
// Инициализируем объект один раз для всей цепочки запросов
WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
// Шаг 1: Авторизация
ЗапросАвторизации = "...XML-пакет с логином...";
WinHttp.open("POST", АдресСервиса, 0);
WinHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
WinHttp.send(ЗапросАвторизации);
// Проверяем статус (должен быть 200 OK)
Если WinHttp.Status = 200 Тогда
// Шаг 2: Запрос данных (объект тот же!)
ЗапросДанных = "...XML-пакет для поиска запчасти...";
// Мы просто открываем новое соединение в том же объекте
WinHttp.open("POST", АдресСервиса, 0);
WinHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
// Куки передадутся автоматически!
WinHttp.send(ЗапросДанных);
Результат = WinHttp.ResponseText;
КонецЕсли;
Иногда веб-сервисы работают нестандартно, или требуется передать куки между разными сеансами работы программы. В таком случае мы можем извлечь куки вручную. Важно помнить, что метод getResponseHeader("Set-Cookie") может вернуть только одну куку, если их пришло несколько. Для получения всех заголовков лучше использовать анализ полного текста заголовков.
Выясним причину, по которой ручная передача может не работать. Стандарт HTTP требует, чтобы в ответе сервера каждая кука шла в отдельной строке Set-Cookie, а в запросе клиента все куки должны быть перечислены в одной строке Cookie через точку с запятой.
Пример формирования заголовка вручную:
// Получаем все заголовки ответа
ВсеЗаголовки = WinHttp.getAllResponseHeaders();
// Логика извлечения строк "Set-Cookie: ..."
// После извлечения объединяем их в формат: "name1=value1; name2=value2"
НужныеКуки = "session_id=12345; user_id=987";
// При следующем запросе устанавливаем заголовок
WinHttp.open("POST", АдресСервиса, 0);
WinHttp.SetRequestHeader("Cookie", НужныеКуки);
WinHttp.send(Данные);
Если вы работаете на платформе 1С версии 8.3, настоятельно рекомендуется отказаться от использования COM-объектов в пользу встроенного объекта HTTPСоединение. Это более надежно, так как код будет работать и на Windows, и на Linux (в случае серверного вызова), а также исключает проблемы с правами доступа к COM-компонентам.
Объект HTTPСоединение также умеет поддерживать сессию (хранить куки) автоматически в рамках жизни одного объекта. Проанализируем, как это реализовать:
// Создаем соединение
Соединение = Новый HTTPСоединение("api.parts-service.ru", , , , , , Новый ЗащищенноеСоединениеOpenSSL());
// 1. Формируем запрос на авторизацию
ЗапросАвторизации = Новый HTTPЗапрос("/soap/auth");
ЗапросАвторизации.УстановитьТелоИзСтроки(ТекстXML, КодировкаТекста.UTF8);
ЗапросАвторизации.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
Ответ = Соединение.ОтправитьДляОбработки(ЗапросАвторизации);
// 2. Выполняем следующий запрос через то же Соединение
ЗапросДанных = Новый HTTPЗапрос("/soap/search");
ЗапросДанных.УстановитьТелоИзСтроки(ТекстXML_Поиск, КодировкаТекста.UTF8);
ЗапросДанных.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
// Соединение само подставит куки из предыдущего ответа
ОтветДанные = Соединение.ОтправитьДляОбработки(ЗапросДанных);
РезультатXML = ОтветДанные.ПолучитьТелоКакСтроку();
Если код кажется верным, но авторизация все равно не проходит, воспользуйтесь следующими советами:
User-Agent или Referer).WinHttp и ручном формировании XML-пакетов, убедитесь, что длина строки вычисляется корректно. Если в строке есть кириллица, обычная функция СтрДлина() может вернуть количество символов, в то время как серверу нужно количество байт (зависит от кодировки UTF-8).WinHttp может блокировать запрос. Для этого используйте опцию Option(4) для игнорирования ошибок сертификатов.Подведем итог: работа с Cookie в 1С требует соблюдения принципа неразрывности объекта соединения. Если вы используете один и тот же экземпляр WinHttp или HTTPСоединение для всей цепочки вызовов, платформа берет управление сессией на себя, избавляя вас от необходимости вручную парсить заголовки Set-Cookie.