Как решить проблему с передачей дат и авторизацией при интеграции 1С с внешним веб-сервисом WCF?

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

При интеграции платформы 1С:Предприятие со сложными внешними SOAP/WCF веб-сервисами разработчики часто сталкиваются с двумя серьезными препятствиями: специфической авторизацией уровня сообщения (например, TransportWithMessageCredential с токенами WS-Security UserNameToken) и некорректной передачей дат, когда день и месяц самопроизвольно меняются местами или вызывают ошибку при значениях дня больше 12.

Разберем детально, почему возникают эти ошибки, как правильно настроить передачу параметров даты через .NET-мост и какие существуют альтернативные способы прямого взаимодействия из 1С без использования внешних библиотек.

Анализ причин некорректной обработки дат

Выясним причину, по которой дата 01.12.2017 превращается в 12.01.2017, а запросы с датой после 12-го числа месяца завершаются ошибкой:

  1. Неявная строковая сериализация и региональные настройки: При передаче значения типа Дата из 1С в управляемый код .NET через COM-маршалинг значение может преобразовываться в строку. Если вызывающий процесс или поток .NET инициализирован с культурой en-US или InvariantCulture, строковое представление даты формата дд.мм.гггг интерпретируется по американскому стандарту ММ/дд/гггг. Если день равен, например, 15, парсер не может распознать 15-й месяц и выдает ошибку.
  2. Неявное приведение типов: При вызове сгенерированных методов WCF-клиента передача строкового представления вместо строго типизированного System.DateTime приводит к сбоям сериализации SOAP-пакета.
  3. Ограничения платформы 1С при работе с WCF: Встроенные механизмы WSСсылки и WSПрокси в 1С не поддерживают привязку TransportWithMessageCredential (передача логина и пароля в заголовке wsse:Security по защищенному каналу HTTPS), из-за чего разработчикам приходится использовать внешние прокси-компоненты или формировать запросы вручную.

Способ 1. Явное создание объекта System.DateTime через .NET COM-обертку

Если интеграция реализована через вспомогательную библиотеку (например, NetObjectToIDispatch или собственную COM-видимую сборку C#), необходимо исключить неявное преобразование дат в строки. Создадим объект System.DateTime явно, передав числовые составляющие даты (год, месяц, день, часы, минуты, секунды).

Посмотрим на пример правильного формирования даты в 1С:


// Получаем доступ к типу DateTime в среде CLR
ТипДатаВремя = Врап.ПолучитьТип("System.DateTime");

// Создаем объект строго по числовым параметрам (Год, Месяц, День, Час, Минута, Секунда)
ИсходнаяДата = ТекущаяДата();
ДатаДляСервиса = Врап.СоздатьОбъект(ТипДатаВремя, 
    Год(ИсходнаяДата), 
    Месяц(ИсходнаяДата), 
    День(ИсходнаяДата), 
    Час(ИсходнаяДата), 
    Минута(ИсходнаяДата), 
    Секунда(ИсходнаяДата));

// Передаем строго типизированную дату в метод веб-сервиса
Результат = КлиентСервиса.GetInvoices(ДатаДляСервиса);

Также можно воспользоваться методом принудительного приведения типов ChangeType:

Дата1 = Врап.ChangeType("System.DateTime", ТекущаяДата());

Важно: Передача даты отдельными числовыми компонентами полностью исключает влияние локали потока (CultureInfo) и гарантирует правильную сериализацию дня и месяца.

Способ 2. Строгий парсинг на стороне C#

Если вы пишете собственную библиотеку-адаптер на C# для вызова WCF-сервиса, проанализируем, как правильно принимать строковые параметры из 1С. Избегайте использования стандартного DateTime.Parse без указания формата.

Рассмотрим пример безопасного парсинга в коде C#:


public InvoiceResult[] GetInvoicesByDateString(string dateString)
{
    // Строго задаем формат и культуру для исключения путаницы дней и месяцев
    DateTime parsedDate;
    string[] formats = { "yyyy-MM-ddTHH:mm:ss", "yyyy-MM-dd", "dd.MM.yyyy HH:mm:ss", "dd.MM.yyyy" };
    
    if (!DateTime.TryParseExact(dateString, formats, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out parsedDate))
    {
        throw new ArgumentException("Неверный формат даты: " + dateString);
    }
    
    return _client.GetInvoices(parsedDate);
}

Способ 3. Прямой SOAP-запрос через HTTPСоединение (без сторонних DLL)

Поскольку встроенный WSПрокси не поддерживает заголовок безопасности TransportWithMessageCredential, наиболее надежным и кроссплатформенным решением для 1С является формирование сырого SOAP-запроса через стандартный объект HTTPСоединение.

Разберем по шагам реализацию этого подхода:

  1. Сформируем тело SOAP-конверта с заголовком wsse:Security.
  2. Сконвертируем даты в стандартный формат ISO 8601 с помощью встроенной функции XMLСтрока.
  3. Отправим POST-запрос с помощью HTTPЗапрос.

Рассмотрим программную реализацию:


Сервер = "api-efactura.fisc.md";
Ресурс = "/Service.svc";

// Формируем дату по стандарту XML (yyyy-MM-ddTHH:mm:ss)
ДатаЗапроса = XMLСтрока(ТекущаяДата());

// Формируем тело SOAP-конверта с авторизацией WS-Security
ТекстЗапроса = 
"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:tem=""http://tempuri.org/"" xmlns:oas=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">" +
"   <soapenv:Header>" +
"      <oas:Security>" +
"         <oas:UsernameToken>" +
"            <oas:Username>supplier</oas:Username>" +
"            <oas:Password>supplier</oas:Password>" +
"         </oas:UsernameToken>" +
"      </oas:Security>" +
"   </soapenv:Header>" +
"   <soapenv:Body>" +
"      <tem:GetInvoices>" +
"         <tem:date>" + ДатаЗапроса + "</tem:date>" +
"      </tem:GetInvoices>" +
"   </soapenv:Body>" +
"</soapenv:Envelope>";

SSL = Новый ЗащищенноеСоединениеOpenSSL();
HTTPСоединение = Новый HTTPСоединение(Сервер, 443, , , , 30, SSL);

HTTPЗапрос = Новый HTTPЗапрос(Ресурс);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
HTTPЗапрос.Заголовки.Вставить("SOAPAction", "http://tempuri.org/IService/GetInvoices");
HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);

Если HTTPОтвет.КодСостояния = 200 Тогда
    ОтветXML = HTTPОтвет.ПолучитьТелоКакСтроку();
    // Обработка полученного XML через ЧтениеXML или ФабрикуXDTO
Иначе
    ВызватьИсключение "Ошибка сервиса: " + HTTPОтвет.КодСостояния + " " + HTTPОтвет.ПолучитьТелоКакСтроку();
КонецЕсли;

Преимущества прямого HTTP-запроса:

  1. Функция XMLСтрока(Дата) всегда формирует канонический формат даты ГГГГ-ММ-ДДTЧЧ:ММ:СС, что исключает ошибки региональных настроек операционной системы.
  2. Код полностью автономен, работает как под Windows, так и под Linux, и не требует регистрации сторонних COM-библиотек или установки .NET Framework на сервере 1С.
← На главную