Как исправить ошибку разбора XML и WSDL при отправке SMS из мобильного приложения 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке мобильных приложений на платформе 1С:Предприятие 8 разработчики часто сталкиваются с необходимостью интеграции со сторонними веб-службами (например, SMS-шлюзами, логистическими службами или государственными информационными системами). Традиционным и привычным способом интеграции в экосистеме 1С является использование веб-сервисов SOAP через встроенный механизм WSОпределения. Однако при попытке перенести работающий на настольном компьютере код в мобильное приложение на устройствах под управлением Android или iOS может возникнуть критическая ошибка разбора XML, полностью блокирующая работу механизма.

В данной статье мы подробно проанализируем причины этой проблемы, выясним, почему один и тот же код ведет себя по-разному на персональном компьютере и мобильном устройстве, а также разберем три практических способа решения этой задачи.

Почему возникает ошибка разбора XML на мобильных устройствах?

Типичный сценарий: на стационарном компьютере или сервере 1С под управлением ОС Windows код инициализации веб-сервиса отрабатывает без нареканий, но при запуске на планшете или смартфоне та же самая строка кода вызывает аварийное завершение процесса:

Определение = Новый WSОпределения("https://sms4b.ru/webservices/sms.asmx?WSDL");

Платформа генерирует ошибку следующего вида:

Ошибка при вызове конструктора (WSОпределения): Ошибка разбора XML: [2,190] Ошибка: xmlns:tns: 'SMS client' is not a valid URI

Давайте проанализируем ситуацию и выясним истинную техническую причину такого поведения:

  1. Различия в XML-движках операционных систем. Настольная платформа 1С для Windows использует встроенные системные библиотеки Microsoft (такие как MSXML), которые исторически обладают очень высокой степенью лояльности («всеядности») к ошибкам разметки и мелким нарушениям стандартов XML. Мобильная же платформа 1С на Android и iOS опирается на стандартные мобильные системные библиотеки парсинга (например, XmlPullParser в Android), строго следующие спецификациям консорциума W3C.
  2. Невалидный URI пространства имен в WSDL. В XML-схеме (WSDL) стороннего сервиса пространство имен (namespace) может быть объявлено с использованием пробелов (например, xmlns:tns="SMS client"). Согласно строгим стандартам W3C и RFC 3986, значение атрибута xmlns должно представлять собой валидный URI (Uniform Resource Identifier), в котором использование пробелов абсолютно недопустимо. Строгий мобильный парсер видит пробел в строке 'SMS client', считает спецификацию WSDL некорректной и немедленно прекращает импорт схемы, в то время как Windows-версия просто игнорирует эту неточность.

Вариант 1: Имитация SOAP-запросов через HTTP-соединение

Рассмотрим самый надежный способ обхода этой ошибки, не требующий изменения серверной части стороннего провайдера или ожидания исправлений от его техподдержки. Проанализируем суть технологии SOAP: любой SOAP-запрос — это обычный POST-запрос по протоколу HTTP(S), в теле которого передается XML-пакет определенной структуры (так называемый SOAP-конверт или Envelope).

Мы можем полностью отказаться от капризного конструктора WSОпределения и сформировать запрос вручную. Разберем этот процесс по шагам:

  1. Сформируем XML-тело запроса с необходимыми параметрами (логин, пароль, текст сообщения, получатель).
  2. Настроим защищенное соединение с использованием объекта ЗащищенноеСоединениеOpenSSL.
  3. Создадим объект HTTPСоединение и подготовим HTTPЗапрос.
  4. Укажем обязательные заголовки запроса (включая Content-Type и SOAPAction).
  5. Отправим запрос и разберем полученный XML-ответ через ЧтениеXML.

Посмотрим на пример практической реализации этого метода:


Функция ОтправитьSMSЧерезHTTPSOAP(НомерТелефона, ТекстСообщения) Экспорт
    
    // Шаг 1. Формируем тело SOAP-конверта в виде XML-строки
    ТелоЗапроса = 
    "<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" " + Символы.ПС +
    "xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" " + Символы.ПС +
    "xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" + Символы.ПС +
    "  <soap:Body>" + Символы.ПС +
    "    <SendSMS xmlns=""http://sms4b.ru/ws/"">" + Символы.ПС +
    "      <Login>МойЛогин</Login>" + Символы.ПС +
    "      <Password>МойПароль</Password>" + Символы.ПС +
    "      <SourceAddress>MyBrand</SourceAddress>" + Символы.ПС +
    "      <DestinationAddress>" + НомерТелефона + "</DestinationAddress>" + Символы.ПС +
    "      <Text>" + ТекстСообщения + "</Text>" + Символы.ПС +
    "    </SendSMS>" + Символы.ПС +
    "  </soap:Body>" + 
    "</soap:Envelope>";
    
    // Шаг 2. Инициализируем защищенное соединение для работы по HTTPS
    ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
    
    // Шаг 3. Настраиваем подключение к серверу
    АдресСервера = "sms4b.ru";
    Таймаут = 15;
    Соединение = Новый HTTPСоединение(АдресСервера, , , , , Таймаут, ЗащищенноеСоединение);
    
    // Шаг 4. Готовим HTTP-запрос
    Ресурс = "/ws/sms.asmx";
    Запрос = Новый HTTPЗапрос(Ресурс);
    
    // Задаем обязательные HTTP-заголовки для SOAP веб-службы
    Запрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
    Запрос.Заголовки.Вставить("SOAPAction", """http://sms4b.ru/ws/SendSMS""");
    Запрос.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    // Шаг 5. Отправляем запрос и анализируем полученный ответ
    Попытка
        Ответ = Соединение.ОтправитьДляОбработки(Запрос);
        Если Ответ.КодСостояния = 200 Тогда
            // Успешный запрос, разбираем возвращаемый XML
            Возврат РазобратьОтветSOAP(Ответ.ПолучитьТелоКакСтроку());
        Иначе
            ЗаписатьВЖурналРегистрации("Отправка SMS", СистемнаяИнформация.УровеньЖурналаРегистрации.Ошибка, , , 
                "Не удалось отправить запрос. Код ошибки сервера: " + Ответ.КодСостояния);
            Возврат Ложь;
        КонецЕсли;
    Исключение
        ЗаписатьВЖурналРегистрации("Отправка SMS", СистемнаяИнформация.УровеньЖурналаРегистрации.Ошибка, , , 
            "Ошибка сетевого соединения: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат Ложь;
    КонецПопытки;
    
КонецФункции

Этот подход гарантирует абсолютно стабильную работу на любых мобильных устройствах, поскольку он полностью обходит стадию автоматического разбора тяжелого и капризного файла WSDL встроенными средствами платформы 1С и взаимодействует напрямую с эндпоинтом.

Вариант 2: Использование альтернативной WSDL-схемы

Если использование стандартных механизмов 1С для работы с веб-сервисами (таких как WSПрокси) для вас принципиально, и вы не хотите формировать XML вручную, рассмотрим варианты исправления самой схемы:

А. Поиск альтернативного адреса подключения (Endpoint)

Зачастую крупные провайдеры предоставляют несколько версий шлюзов или параллельно поддерживают разные структуры адресов. Например, как показывает практика работы с SMS4B, адрес https://sms4b.ru/webservices/sms.asmx?WSDL содержит в схеме пробелы и вызывает падение парсера 1С на мобильной платформе. В то же время альтернативный адрес https://sms4b.ru/ws/sms.asmx?WSDL базируется на более строгой и чистой XML-схеме, в которой нет недопустимых URI. Попробуйте переключить ваше мобильное приложение на альтернативный адрес.

Б. Локальное размещение исправленной схемы

Если провайдер предоставляет только один WSDL-файл и в нем содержатся ошибки, мы можем исправить схему самостоятельно:

  1. Скачайте WSDL-файл на локальный компьютер через любой веб-браузер.
  2. Откройте его в текстовом редакторе и найдите ошибочное объявление пространства имен (например, замените строку xmlns:tns="SMS client" на валидное xmlns:tns="SMS_client" или укажите реальный URL-адрес без пробелов).
  3. Сохраните исправленный XML-файл на своем веб-сервере или включите его непосредственно в состав макетов вашей конфигурации.
  4. В коде мобильного приложения инициализируйте WSОпределения, указав путь к локальной копии исправленного WSDL-файла на устройстве или URL вашего собственного сервера.

// Пример инициализации из локального макета конфигурации
ПутьКФайлу = ПолучитьИмяВременногоФайла("wsdl");
ДвоичныеДанныеСхемы = ПолучитьМакет("ИсправленнаяСхемаWSDL");
ДвоичныеДанныеСхемы.Записать(ПутьКФайлу);

Определение = Новый WSОпределения(ПутьКФайлу);
// Далее создаем WSПрокси стандартным образом

Вариант 3: Переход на современный REST API с форматом JSON

Использование SOAP-протокола в мобильной разработке постепенно уходит в прошлое. SOAP-конверты создают избыточный сетевой трафик за счет излишне тяжелой структуры XML-тегов, что критично в условиях нестабильного мобильного интернета и быстрее расходует заряд аккумулятора устройства. Современным и производительным стандартом является использование легкого протокола REST API с форматом обмена данными JSON.

Почти все современные SMS-сервисы предоставляют полноценные REST-интерфейсы. Интеграция через REST API в мобильной 1С реализуется чрезвычайно просто и быстро с использованием встроенных объектов HTTPСоединение, ПрочитатьJSON и ЗаписатьJSON. Авторизация при этом происходит не по логину и паролю в каждом запросе, а через безопасные API-токены (Bearer tokens).

Рассмотрим пример отправки SMS через современный REST API с передачей параметров в формате JSON:


Функция ОтправитьSMSЧерезREST(Получатель, ТекстСообщения) Экспорт
    
    ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
    Соединение = Новый HTTPСоединение("api.sms-service-provider.ru", , , , , 10, ЗащищенноеСоединение);
    
    // Подготовка структуры запроса согласно спецификации API
    ПараметрыЗапроса = Новый Структура;
    ПараметрыЗапроса.Вставить("sender", "MyBrand");
    ПараметрыЗапроса.Вставить("phone", Получатель);
    ПараметрыЗапроса.Вставить("text", ТекстСообщения);
    
    // Сериализация структуры в строку JSON
    ЗаписьJSON = Новый ЗаписьJSON;
    ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, " ", Истина);
    ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
    ЗаписатьJSON(ЗаписьJSON, ПараметрыЗапроса);
    СтрокаJSON = ЗаписьJSON.Закрыть();
    
    // Создание HTTP-запроса
    Запрос = Новый HTTPЗапрос("/v1/sms/send");
    Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
    
    // Используем современную токенизированную авторизацию
    Запрос.Заголовки.Вставить("Authorization", "Bearer d83b7f29ac02e8813a48e77c08271e");
    Запрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    Попытка
        Ответ = Соединение.ОтправитьДляОбработки(Запрос);
        Если Ответ.КодСостояния = 200 Тогда
            // SMS успешно отправлено
            Возврат Истина;
        Иначе
            ЗаписатьВЖурналРегистрации("Отправка SMS REST", СистемнаяИнформация.УровеньЖурналаРегистрации.Предупреждение, , , 
                "Ответ сервера: " + Ответ.ПолучитьТелоКакСтроку());
            Возврат Ложь;
        КонецЕсли;
    Исключение
        ЗаписатьВЖурналРегистрации("Отправка SMS REST", СистемнаяИнформация.УровеньЖурналаРегистрации.Ошибка, , , 
            "Сбой соединения: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат Ложь;
    КонецПопытки;
    
КонецФункции

Резюме и рекомендации

Подводя итоги, сформулируем основные правила работы с внешними API в мобильной платформе 1С:

← На главную