При интеграции платформы 1С с внешними веб-сервисами разработчики часто сталкиваются с архитектурным ограничением штатного механизма WSПрокси. Этот объект отлично справляется со стандартными SOAP-запросами, но совершенно не умеет работать с состоянием сессии (держать сессию) и автоматически передавать Cookie-контейнеры, как это реализовано, например, в среде .NET с помощью класса CookieContainer.
Если веб-сервис требует обязательной авторизации с последующим сохранением сессии в Cookie (как это происходит при работе с сервисом поиска автозапчастей «Автопитер» и многими другими API), использование прямого вызова методов через WSПрокси приведет к ошибке авторизации или блокировке по количеству запросов. Разберем детально, почему возникает эта проблема и как ее правильно решить в 1С.
Рассмотрим стандартный сценарий взаимодействия с защищенным веб-сервисом:
Authorization), передавая логин и пароль.Set-Cookie с уникальным идентификатором сессии (например, AuthCoocies=010280AA...).FindCatalog) клиент обязан прикреплять полученную куку в заголовке Cookie каждого HTTP-запроса. Если этого не сделать, сервер посчитает запрос неавторизованным.Поскольку WSПрокси в 1С выполняет каждый запрос «с чистого листа» без сохранения заголовков предыдущих ответов, веб-сервис считает каждый ваш запрос попыткой неавторизованного доступа. В результате срабатывают защитные алгоритмы сервера, и вы получаете ошибку: «Превышено количество запросов к веб сервису. Вы блокированы на текущие сутки».
Для обхода этого ограничения нам придется отказаться от высокоуровневого WSПрокси и перейти на работу с «сырыми» HTTP-запросами. Мы можем реализовать это двумя способами: кроссплатформенным методом через встроенный объект 1С HTTPСоединение или с помощью COM-объекта WinHttpRequest (только для операционных систем Windows).
---
Использование встроенных объектов 1С HTTPСоединение и HTTPЗапрос — наиболее правильный и современный подход. Этот код будет одинаково успешно работать как на Windows-клиентах, так и на серверах под управлением Linux.
Разберем процесс по шагам. Сначала нам необходимо отправить SOAP-конверт авторизации, перехватить заголовки ответа, очистить полученные куки и подставить их в следующий SOAP-запрос.
Важный нюанс: Сервер присылает куки со множеством служебных параметров (срок действия expires, путь path, флаг безопасности HttpOnly). Если передать их обратно серверу «как есть», сервер может выдать ошибку авторизации. Нам нужно очистить строку куки, оставив только саму пару «имя=значение».
Рассмотрим пример реализации:
// Шаг 1. Настройка соединения с сервером
ИмяСервера = "service.autopiter.ru";
АдресРесурса = "/price.asmx"; // Путь к SOAP-сервису
// Используем защищенное соединение SSL, если сервис работает по HTTPS
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение(ИмяСервера, , , , , , ЗащищенноеСоединение);
// Шаг 2. Формируем тело первого запроса (Авторизация)
// Конверт SOAP формируется вручную в виде XML-строки
ТелоАвторизации =
"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:aut=""http://www.autopiter.ru/"">
| <soapenv:Header/>
| <soapenv:Body>
| <aut:Authorization>
| <aut:login>ВашЛогин</aut:login>
| <aut:password>ВашПароль</aut:password>
| <aut:isDealer>true</aut:isDealer>
| </aut:Authorization>
| </soapenv:Body>
|</soapenv:Envelope>";
ЗапросАвторизации = Новый HTTPЗапрос(АдресРесурса);
ЗапросАвторизации.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
ЗапросАвторизации.Заголовки.Вставить("SOAPAction", "http://www.autopiter.ru/Authorization");
ЗапросАвторизации.УстановитьТелоИзСтроки(ТелоАвторизации, КодировкаТекста.UTF8);
// Отправляем запрос авторизации
ОтветАвторизации = Соединение.ОтправитьДляОбработки(ЗапросАвторизации);
Если ОтветАвторизации.КодСостояния = 200 Тогда
// Получаем сырые данные куки из заголовков ответа
СырыеКуки = ОтветАвторизации.Заголовки.Получить("Set-Cookie");
Если ЗначениеЗаполнено(СырыеКуки) Тогда
// Очищаем куки от параметров expires, path и т.д.
// Нам нужно забрать все, что идет до первой точки с запятой
ПозицияРазделителя = Найти(СырыеКуки, ";");
Если ПозицияРазделителя > 0 Тогда
ЧистыеКуки = Лев(СырыеКуки, ПозицияРазделителя - 1);
Иначе
ЧистыеКуки = СырыеКуки;
КонецЕсли;
// Шаг 3. Выполняем полезный запрос с передачей куки
ТелоЗапросаДанных =
"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:aut=""http://www.autopiter.ru/"">
| <soapenv:Header/>
| <soapenv:Body>
| <aut:FindCatalog>
| <aut:ShortNumberDetail>OC47</aut:ShortNumberDetail>
| </aut:FindCatalog>
| </soapenv:Body>
|</soapenv:Envelope>";
ЗапросДанных = Новый HTTPЗапрос(АдресРесурса);
ЗапросДанных.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
ЗапросДанных.Заголовки.Вставить("SOAPAction", "http://www.autopiter.ru/FindCatalog");
// Внедряем очищенную авторизационную куку в заголовок запроса
ЗапросДанных.Заголовки.Вставить("Cookie", ЧистыеКуки);
ЗапросДанных.УстановитьТелоИзСтроки(ТелоЗапросаДанных, КодировкаТекста.UTF8);
ОтветДанных = Соединение.ОтправитьДляОбработки(ЗапросДанных);
Если ОтветДанных.КодСостояния = 200 Тогда
ТекстОтвета XML = ОтветДанных.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
// Далее разбираем XML с помощью ЧтениеXML или ПостроительDOM
ВывестиРезультат(ТекстОтветаXML);
Иначе
ВызватьИсключение "Ошибка запроса данных: " + ОтветДанных.КодСостояния;
КонецЕсли;
Иначе
ВызватьИсключение "Сервер не вернул авторизационную куку Set-Cookie!";
КонецЕсли;
Иначе
ВызватьИсключение "Ошибка авторизации. Код состояния: " + ОтветАвторизации.КодСостояния;
КонецЕсли;
---
Если ваша база работает исключительно в среде Windows и вам по каким-то причинам удобнее использовать COM-объекты операционной системы, можно прибегнуть к помощи библиотеки WinHttp.WinHttpRequest.5.1.
Основная ошибка при использовании этого COM-объекта в 1С заключается в том, что разработчики часто пересоздают экземпляр объекта WinHttp для каждого нового запроса (как это было показано в Сообщении 9 исходной темы). При пересоздании объекта полностью теряется внутренний контекст сессии Windows HTTP Services.
Правильный подход предполагает либо использование одного и того же живого COM-объекта, либо корректное извлечение и ручную подстановку очищенных куки. Посмотрим на пример реализации с ручным извлечением:
АдресСервиса = "http://service.autopiter.ru/price.asmx";
// 1. Создаем COM-объект
WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
// Инициируем запрос авторизации
WinHttp.open("POST", АдресСервиса, ЛОЖЬ); // ЛОЖЬ - синхронный запрос
WinHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
WinHttp.setRequestHeader("SOAPAction", "http://www.autopiter.ru/Authorization");
ТелоАвторизации = "<soapenv:Envelope ...>...</soapenv:Envelope>"; // XML-конверт авторизации
WinHttp.send(ТелоАвторизации);
// Получаем строку куки из заголовков ответа
ВходящиеКуки = WinHttp.getResponseHeader("Set-cookie");
// Очищаем куки от лишних параметров
Поз = Найти(ВходящиеКуки, ";");
Если Поз > 0 Тогда
ЧистыеКуки = Лев(ВходящиеКуки, Поз - 1);
Иначе
ЧистыеКуки = ВходящиеКуки;
КонецЕсли;
// 2. Выполняем второй запрос (поиск) в том же или новом объекте
// Но в заголовок передаем только отформатированную куку
ТелоПоиска = "<soapenv:Envelope ...>...</soapenv:Envelope>"; // XML-конверт FindCatalog
WinHttp.open("POST", АдресСервиса, ЛОЖЬ);
WinHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
WinHttp.setRequestHeader("SOAPAction", "http://www.autopiter.ru/FindCatalog");
WinHttp.setRequestHeader("Cookie", ЧистыеКуки); // Передаем очищенный Cookie
WinHttp.send(ТелоПоиска);
Результат = WinHttp.responseText;
---
Для того чтобы ваша интеграция работала стабильно и не вызывала блокировок со стороны безопасности веб-сервиса, обязательно примените на практике следующие рекомендации:
expires). Не нужно проходить авторизацию и получать новую куку при каждом поиске запчасти! Это перегружает сервер и ведет к блокировкам. Правильно сохранять полученную куку в 1С (в константу или специальный регистр сведений) вместе с датой ее получения. При поиске деталей подставляйте сохраненную куку. Запрос на авторизацию выполняйте повторно только тогда, когда кука устареет или веб-сервис вернет ошибку сессии./price.asmx) считается устаревшей и поддерживается по остаточному принципу. Рекомендуется переписать интеграцию на современную версию API V2 (работающую по протоколу JSON REST или обновленному SOAP V2 по адресу /v2/price). Протокол REST API значительно проще в разборе и изначально проектируется под удобную передачу токенов авторизации в заголовках.Применение любого из предложенных вариантов передачи заголовков Cookie позволит вам полностью решить проблему авторизации в 1С и настроить стабильный обмен данными с любым внешним веб-сервисом.