Каталог решений - Рушим стереотипы о HTTP и XML. (XMLHTTP и MSXML2) На примере API Новая Почта

Рушим стереотипы о HTTP и XML. (XMLHTTP и MSXML2) На примере API Новая Почта

Рушим стереотипы о HTTP и XML. (XMLHTTP и MSXML2) На примере API Новая Почта

В наличии

За последние 2 года встречалось много конфигураций, с морально-устаревшими механизмами, написанные гуру старой закалки. В свое время подобные методы были шагом вперед, за что и «респект» их творцам, но время не стоит на месте… Более всего меня угнетает «копи-паст» кода. У большинства 1с-ников со стажем есть целые библиотеки нужных вещиц… Но за 2-3 года все устаревает, а вот библиотечки то остаются без прогресса…
Но это еще пол беды… Самое неприятное — это когда «молодые творцы», тем же «копи-пастом», строят целые схемы обменов, наращивая код до полной не читабельности.
Решил поделится одним из подобных «атавизмов»… В данной статье подробно опишу схему работы с API посредством XMLHTTP и MSXML2, самым оптимальным (из моего опыта) способом.

Категория:

Описание

UPD 11.2017

Статье больше 2-х лет. Лично я сейчас практически для всего уже использую ElisyNetBridge, гружу ассамблею и ни в чем себе не отказываю. Да и встроенные механизмы 8.3 внушают доверие, касательно кроссплатформенности. Статья на момент написания уже немного уступала прогрессу, а сейчас и подавно. Но кто использует старые платформы с той или иной причины — будет все равно полезно, поэтому публикацию не скрываю. Стандартными механизмами пост запросы иногда без бубна не взлетают, с телом запроса постоянно нужно тыкаться, даже в 838, таки юзал хмлхттп недавно. А в целом — статья морально устаревшая. Да и АПИ Новой Почты уже перешагнуло отметку беты и увидела мир вторая версия.

С чего бы начать… Наверное с самой сути общения 1с и АПИ.

Википедия говорит, что АПИ это  "набор готовых классовпроцедурфункцийструктур и констант, предоставляемых приложением (библиотекой, сервисом) для использования во внешних программных продуктах. Используется программистами для написания всевозможных приложений."

Физически это дело можно сравнить с сервисом, слушающий POST запросы и отвечает источнику в соответствии с определенными сервером правилами. (не буду углублятся)

Значит суть весьма проста: По HTTP-протоколу отправить POST-запрос и дождатся ответа.

Теперь время немного углубиться… 
Зачастую я встречал такой принцип в большинстве конфигураций: 

1. Генерим XML с помощью Новый ЗаписьXML, сохраняем во временную папку.
2. Открываем НовыйHTTPСоединение() и "пихаем" в него нашу XML.

ОтправитьДляОбработки(ФайлXML, РесурсНаСервере, ФайлРезультата)

3. Получаем XML ответ. 
4. В рекурсии обходим сие багатство с помощью  Новый ЧтениеXML() и складываем все в дерево значений.
5. Опять в рекурсии обходим дерево для получения определенных данных…

 

Ну просто мраки… По 100500 раз дергать файловую систему, сорить в кэше и забивать оперу временной всячиной во время обхода потенциально не нужных вещей…

Теперь в кратце, что мы будем оптимизировать на примере апи новой почты:
1. Общение с сервисом переводим на более стабильные xmlhttp
2. Для быстрого получения списка отделений — уходим от универсальности и "рекурсии на все случаи жизни", используем свой парсер msxml2.
4. Так же уйдем от сохранения временных файлов. будем использовать тип "строка" для чтения и передачи XML.
5. Научимся использовать синхронные запросы xmlhttp.

 1. Функция отправки запроса. Весьма легкая на подъем на WIN-осях (Совсем забыл уточнить, о никсах сегодня речь идти не будет)

Функция ОтправитьЗапрос(ТекстXML)
   Адрес = "http://orders.novaposhta.ua/xml.php";
   Попытка 
      XMLHTTP = GetCOMObject("", "Microsoft.XMLHTTP");
      XMLHTTP.Open("POST", Адрес, Ложь);
      XMLHTTP.SetRequestHeader("Content-Type", "text/xml");
      XMLHTTP.Send(ТекстXML);
      Возврат XMLHTTP.ResponseText;
   Исключение 
      Возврат Неопределено
   КонецПопытки;
КонецФункции

Функция вернет ResponseText, в нашем случае, это такой же текст XML, как мы отправляли, который нам необходимо прочитать и обработать. И тут начинается настоящая магия =)

Что бы не отвалиться на пустом ответе — проверим код ответа.

Функция ПрочитатьResponseTextXML(ResponseText)
   xmlParser= новый COMОбъект("msxml2.domdocument.6.0"); //Подняли ком 
   xmlParser.loadXML(ResponseText); // Сказали прочитать текст XML
   responseCode = xmlParser.selectSingleNode("//response/responseCode").text;//Нашли представление узла по абсолютному пути
   Возврат responseCode; // Вернули полученное значение
КонецФункции

xmlParser ведет себя весьма дружелюбно при чтении пустой строки или некорректного ответа. И на инглише отвечает нам исключением, если что… Но подстраховатся не помешает, темболее это лишит нас лишних дальнейших"телодвижений"

Следующая функция отправляет запрос на получения подразделений и парсит уже с отбором по городам. (иногда хранить всю иерархию подразделений и городов не охота + синхронизировать постоянно, а ждать загрузки всего этого добра еще менее охотней), по этому самое оптимальное решение — получать только то, что нужно и не более… Об этом немного ниже. 

Функция ПолучитьТаблицуОтделений(АПИКлюч,Город) Экспорт
   
   ОтветXMLHTTP = ОтправитьЗапрос(ТекстXML);
   Если ОтветXMLHTTP = Неопределено Тогда 
      Возврат Неопределено;
   КонецЕсли;
   ТаблицаРезультат = ПрочитатьXMLПодразделений(ОтветXMLHTTP, Город);
   Возврат ТаблицаРезультат;
КонецФункции

ОтветXMLHTTP, при успешном соединении, содержит в себе текст XML, над которым будем танцевать с бубном, что бы пропарсить как можно быстрее. 

Функция ПрочитатьXMLПодразделений(XMLString, Город)
   ТаблицаРезультат = Новый ТаблицаЗначений; // Инициализируем ТЗ
   ТаблицаРезультат.Колонки.Добавить("Город");
   ТаблицаРезультат.Колонки.Добавить("Адрес");
   ТаблицаРезультат.Колонки.Добавить("НомерОтделения");

   xmlParser= новый COMОбъект("msxml2.domdocument.6.0"); // Поднимем КОМ
   xmlParser.loadXML(XMLString);

   responseCode = xmlParser.selectSingleNode("//response/responseCode").text; // Получим значение узла responseCode
   Если responseCode = "200" Тогда
      colNodes = xmlParser.selectNodes("//response/result/whs/warenhouse[cityRu='"+Город+"']");
      // конструкция [cityRu='"+Город+"'] вернет мне массив узлов уровня warenhouse, где cityRu равен Город
      Если colNodes.length = 0 Тогда
         // если совпадений на ру - нет, то ищем названия на укр.
         colNodes = xmlParser.selectNodes("//response/result/whs/warenhouse[city='"+Город+"']"); 
      КонецЕсли;
      Для Каждого ElementNodes из colNodes Цикл
         НоваяСтрока = ТаблицаРезультат.Добавить();
         НоваяСтрока.Город = ElementNodes.childNodes.item(3).text;
         НоваяСтрока.Адрес = ElementNodes.childNodes.item(6).text;
         НоваяСтрока.НомерОтделения = ElementNodes.childNodes.item(7).text;
      КонецЦикла;
   КонецЕсли;

   Возврат ТаблицаРезультат;

КонецФункции

И напоследок призент =)

Состояние заказа… Видел я как-то 3-х этажную функцию по получению статуса… Вот думаю, поделюсь более свежей. 

 

Функция ПолучитьСостояниеЗаказа(НомерЗаказа, АПИКлюч) Экспорт

   
   ОтветXMLHTTP = ОтправитьЗапрос(ТекстXML);
   Если ОтветXMLHTTP = Неопределено Тогда 
      Возврат "";
   КонецЕсли;

   xmlParser= новый COMОбъект("msxml2.domdocument.6.0");
   xmlParser.loadXML(ОтветXMLHTTP);

   responseCode = xmlParser.selectSingleNode("//file/document");

   Если responseCode <> Неопределено Тогда
      Статус = responseCode.getAttribute("status");
      Если Статус <> Неопределено Тогда
         Возврат Статус;
      КонецЕсли;
   КонецЕсли;

   Возврат "";
КонецФункции

Надеюсь потратил не зря время на написание сего труда и пару советов все же почерпнуть можно =)))
Лично в моем случае прирост быстродействия, в последней измененной конфигурации, в среднем составил 400-500 % 

З.Ы. Прошу прощение за скрины с xml, при сохранении все тэги удалялись… Не знаю с чем связано… 

has been added to your cart:
Оформление заказа