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

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

UPD 11.2024

Статье больше 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, при сохранении все тэги удалялись… Не знаю с чем связано… 

34 Comments

  1. Rothschild

    Быстродействие — это наше все!

    ***

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



    Больше камней, больше памяти (всякой)…вместо оптимизации програмного кода

    Reply
  2. B2B

    (0) А ничего, что Ваш код стал платформозависимым?

    Reply
  3. cool.vlad4

    вранье какое-то…как раз наоборот раньше использовали XMLHTTP и MSXML2 (по моим ответам на форуме 2-3 годичной давности посмотрите, да что уж там, поиском любым воспользуйтесь) вовсю, в 8-ке (в типовых даже), и особенно в 7-ке. сейчас же стараются использовать встроенные методы, поскольку их поддержка конечному 1С-нику обходится куда дешевле, — оно работает на разных платформах, заявленных 1С-ом.

    ЗЫ и чего-то сомневаюсь, что msxml2.domdocument.6.0 такое «легкое на подъем на вин-осях», это если на данной винде установлен msxml нужной версии, что не всегда так.

    вот статья http://infostart.ru/public/60330/ 2009-года, но «время не стоит на месте»…и за почти 5 лет особо ничего не изменилось выходит))))

    Reply
  4. Поручик

    Вот как раз API посредством XMLHTTP или MSXML2 я буду использовать в последнюю очередь, и не из-за окостенелости мозга.

    Reply
  5. Alexander.Shvets

    (4) Поручик,

    А чего же? Что ище в 1с вам поможет добиться асинхронности на тонком клиенте?

    Могли бы уточнить, чем будете пользоваться? Стандартным 1с-совским чтением хмл? Он вообще не работает с множествами…

    (2) B2B,

    Ну вы же спорить не будете, если я скажу, что любое решение в 1с «пилится» под конкретную организацию… И зачем мультиплатформиность в пределах 1-го бизнес-проекта???

    Оптимизация работы в пределах одной системы координат намного точнее, чем оптимизация множеств…

    Мы говорим о разных вещах.

    (3) cool.vlad4,

    Я не про методы, я про конфигурации. Может я на столько невезуч, что мне встречается только не качественный код…

    По поводу типовых конфигураций — на то они и типовые, что бы продавать, а не для того, что бы на них работать. И для более дешевой разработки используются мультиплатформенные методы, а никак не с целью улучшения продукта.

    Reply
  6. sikuda

    О версиях msxml http://support.microsoft.com/kb/269238/ru

    Автору для развития попробовать в html поле

    http://www.w3schools.com/ajax/ajax_xmlhttprequest_send.asp

    Reply
  7. Alexander.Shvets

    (6) sikuda,

    На счет версий, не понял. использую последнюю, 6.0

    В общем на упр. формах надо бы еще поиграться.

    Если читать статью в контексте упр. форм — действительно ересь =)) Тут речь идет об обычном приложении…

    Reply
  8. German

    xmlParser лучше поменять на ВычислитьВыражениеXPath

    И

    Соединение = Новый HTTPСоединение(«http://myTestServer», 8080);
    
    Заголовки = Новый Соответсвие();
    Заголовки.Вставить(«host», «http://myTestServer:8080»);
    
    Запрос = Новый HTTPЗапрос(«/order/500», Заголовки);
    Запрос.УстановитьТелоИзСтроки(«<order>My new order</order>»);
    Соединение.Записать(Запрос);

    Reply
  9. Alexander.Shvets

    (8) German,

    Спасибо. Надо будет попробовать. Но с HTTPСоединение у меня постоянно возникали проблемы… Когда сетевой экран задерживает передачу для проверки — то 1с падает с записью дампа памяти. При том, что хмлхттп — дожидался ответа…

    В любом случае все коменты подтолкнули более глубже ознакомится с вопросом… Жаль, что конструктивом веет лишь от пары человек, которым отдельное спасибо.

    Reply
  10. Yashazz

    (3) (4) Согласен.

    Я так и не уразумел, что мешало разобрать xml-ответ средствами платформы, удобнее которых мало где встречал. Насчёт асинхрона ещё туда-сюда, хотя можно выкрутиться через HTTPRequest, но всё остальное — мрак.

    (8) Кстати, да. Впрочем, XPath из 1С-ников юзают немногие, да и в интернетах говорят, что этот способ не особенно «взлетел».

    И, автор, учи русский язык, пожалуйста.

    Reply
  11. DitriX

    Первая мысль, после прочтения статьи, а особенно после такого громкого заголовка была такая — «и?».

    Т.е. потом почитав камменты я уже понял в чем приколы.

    Я в этом плане полный приверженец СОАПа, ибо он легче на подъем, та и вскоре 1С к тем же методам прикрутят хттп реквесты и прочую лабуду. Так что данную статью можно уже в тот самый архив, о котором писал автор. Дабы младшие — не учились старью 🙂

    А вообще — я когда делал чат для одной фирмы, на обычных формах, я использовал поле хтмл для ввода сообщения, но так как 1С туда пихало свой мусор, в частности версию — ie и прочих приблуд — я обнаружил, что из 200компов — распределение версий всякой фигни настолько значительное, что я потом даже чат переписал на управляемые формы, что бы юзать форматированный документ.

    Это я к чему — «msxml2.domdocument.6.0» и ежи с ними — это просто грех юзать. Вы уже или получайте их через настройки системы, или так и говорите — что ваше решение не только вин зависимое, но и указывайте ряд необходимого софта 🙂

    Reply
  12. nafa

    Нормальная статья, нормальное решение.

    Чего Вы к этой зависимости от Windows прицепились, большинству народа дргуие системы не интересны. Кроме того парсеры они везде почти одинаковые (названия методов немного отличаются), так что изменить под другую системы не проблема, и перенести код с 1С в другую систему тоже не проблема. Главное — алгоритм, а не его реализация.

    Зависимость от 1С же не напрягает почему-то.

    Да и еще не известно что дольше проживет как — 1С или windows.

    Причин использовать парсер от MS может быть достаточно много, например в версии 8.2 нет возможности штатными средствами проверить файл на соответствие схеме.

    Reply
  13. awk

    (11) DitriX, Уже вот-вот. http://v8.1c.ru/o7/201312http/index.htm. И еще легче чем соап будет. Будет РЕСТ.

    Reply
  14. awk

    (12) nafa, Читаей внимательней документацию. Еще как можно.

    Reply
  15. vano-ekt

    (0) а щас засунь сей прогрессивный код на не-мс-сервер 🙂

    Reply
  16. diver.sun

    Мдя, эко вы далеко загнули. На 80% организаций сама возможность забирать из web какие то данные рассматриваются как космос. А вы устаревшая технология, анархизм…

    А вот эта строка «xmlParser.loadXML(XMLString);» если xml файл будет под 200 мб. положит любую машину. Не зря же чтениеXML предоставляет ПОСЛЕДОВАТЕЛЬНЫЙ доступ. Да и ставить дополнительно MSXML 6 который навернет выгрузку регл отчетности в 7-ке проблематично, не надо крушить стереотипы….они кровью писаны.

    Reply
  17. Alexander.Shvets

    (15) vano-ekt, Эмм… Как бы… Использование не мс и не рассматривается… Об этом писал в самой статье.

    (16) diver.sun, это не для организаций «космос», а для ребят, которые автоматизируют «хотелки» руководителей. Самой же автоматизации нет предела.

    На счет хмл файла в 200 мб — это надо быть очень «креативным», что бы писать подобные веб-сервисы, выплёвывающие столько текста. Хм… А 200 мб обходить в цикле — это нормально? Не легче ли «отсеять» только то, что надо, пока объект еще «множество», чем дождатся линейности и ПОСЛЕДОВАТЕЛЬНО обходить такие объемы?

    И что-то сомневаюсь, что loadXML «положит любую» базу. На досуге по эксперементирую…

    Reply
  18. Alexander.Shvets

    (16) diver.sun, Совсем забыл.

    Да и ставить дополнительно MSXML 6

    . MSXML входит в поставку оси. Ни разу не приходилось «регать» какие либо длл для MSXML руками…

    Reply
  19. Трактор

    Метод ОтправитьДляОбработки изменился в версии 8.2. Теперь не обязательно дёргать файловую систему. Ненужно писать запрос и ответ в файлы. Работа встроенными средствами 1С проще и универсальнее.

    HTTPСоединение (HTTPConnection)

    ОтправитьДляОбработки (Post)

    Синтаксис:

    ОтправитьДляОбработки(<HTTPЗапрос>, <ИмяВыходногоФайла>)

    Параметры:

    <HTTPЗапрос> (обязательный)

    Тип: HTTPЗапрос.

    HTTP-запрос.

    <ИмяВыходногоФайла> (необязательный)

    Тип: Строка.

    Имя выходного файла, в который записываются полученные с сервера данные.

    Если не указан или содержит пустую строку, то тело ответа может быть получено из объекта HTTPОтвет.

    Возвращаемое значение:

    Тип: HTTPОтвет.

    Показать

    Reply
  20. Поручик

    (19) Изменился в версии 8.2.18 и позднее. Работать без с файлов отправки можно, если отправляется простой post-запрос, без файлов.

    Reply
  21. opx

    В принципе через HTTPСоединение можно сделать все, кроме:

    1. Переадрезации (в ответ просто выдает 302 ответ). Хотя может у меня просто недостаточно знаний, чтобы организовать переадресацию вручную. Зато вот MSXML делает ее автоматически.

    2. Это даже больше уже к построителю DOM: Иногда не хватает функции innerHTML и outerHTML, которые есть у DOMDocument.

    Reply
  22. borrman

    еще HTTPСоединение очень плохо работает (работало — на последних версиях не использовал его уже) с https

    Reply
  23. Трактор
    Работать без с файлов отправки можно, если отправляется простой post-запрос, без файлов.

    (20) Поручик, прикольно сказал:-)

    Действительно трудно обойтись без работы с файлами, если надо отправить файл. 🙂

    Думаю все поняли что ты хотел сказать, но фраза от этого менее смешной не стала.

    Reply
  24. Поручик

    (22) У человека взлетело сразу

    Reply
  25. WKBAPKA

    Мне статья понравилась. Может реализация кому то и не нравиться, но найти реально живой пример для работы с «Новой Почтой» трудно. А тут сразу скопипастил и проверил. Перестанет работать, перепишу по другому 😉

    У меня как раз та ситуация, о которой пишет автор статьи. Клиент которому нужно решить задачу с обменом данными с НП.

    Автору респект.

    Reply
  26. Land1966

    Также спасибо автору. Статья помогла реализовать обмен с «Новой почтой» на 7.7 (работают люди еще и на этой платформе). Может кому пригодится, работающий код для 7.7:

    Процедура ПолучитьФайлГородов()
    Адрес = «https://api.novaposhta.ua/v2.0/xml/»;
    
    WinHttp = СоздатьОбъект(«Msxml2.ServerXMLHTTP.6.0»);
    WinHttp.Open(«POST», Адрес, 0);
    
    XMLДокументОтпр = «<?xml version=»»1.0″» encoding=»»windows-1251″»?>
    | <file>
    | <apiKey>» + КлючАПИ + «</apiKey>
    | <calledMethod>getCities</calledMethod>
    | <methodProperties />
    | <modelName>Address</modelName>
    | </file>»;
    
    WinHttp.Send(XMLДокументОтпр);  // Параметром передаем POST-данные для страницы
    
    XMLДокументПолуч = Анализатор.СоздатьДокумент();
    XMLДокументПолуч.ЗагрузитьИзСтроки(WinHttp.ResponseText());
    XMLДокументПолуч.Кодировка = «UTF-8»;
    
    XMLДокументПолуч.Записать(ИмяФайла1);
    
    WinHttp = 0;
    КонецПроцедуры
    

    Показать

    Reply
  27. CagoBHuK

    Прочитал по диагонали. Если честно, мне вообще непонятно, зачем программисты используют ЧтениеXML и им подобные, когда есть XSLT. Преобразуйте документ в нативный объект XML 1С, прочитайте его фабрикой и используйте наздоровье, как объект 1С нужного Вам типа (например, Структура).

    Reply
  28. CagoBHuK

    (20) Поручик, прекрасно работает «ОтправитьДляОбработки» и с файлами. У меня весь обмен с ЕГАИС на этом построен, а уж какие ТАМ файлы прилагаются мультипартовые — вообще жуть!

    Reply
  29. Alexander.Shvets

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

    Reply
  30. legzzi

    Друзья подскажите пожалуйста по готовым решениям есть для для рассчета почты РФ и контроль трекинг кода — местонахождения посылки?

    Reply
  31. karapuzzzz

    Почему не JSON? По моему это наиболее прогрессивный формат обмена с HTTP API. А если учесть что LiqPay и Viber вообще не предлагают альтернативы кроме как JSON, то и с НП не вижу смысла заворачиваться. ФабрикаXDTO с легкостью пишет/читает JSON используя для этого вполне понятный объект как «структура». Т.е. даём на вход структуру, а на выходе получаем json и наоборот. А сам файл легче

    Reply
  32. Alexander.Shvets

    (32) ответ давно здесь (29). =)

    Reply
  33. karapuzzzz

    (33)А, ну да. JSON в платформе появился только в 2015г. Спасибо

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *