Генерируем ВнешнееСобытие из внешнего приложения/скрипта web запросом

Простая внешняя компонента, которая позволяет генерировать внешние события в 1С:Предприятие из внешнего приложения/скрипта web запросом.

Описание
Компонента создана по технологии Native API и представляет собой web сервер, который принимает GET запросы и вызывает соответствующие функции платформы.
В качестве основы использовались статья A Simple Webserver in C++ for Windows, а также шаблон внешней компоненты с диска ИТС.
В компоненте реализован вызов следующих функции интерфейса IaddInDefBase:

    • ExternalEvent
    • CleanEventBuffer
    • GetEventBufferDepth
    • SetEventBufferDepth

С описанием вышеперечисленных функций можно ознакомиться здесь.

Поддерживаемые ОС

  • Linux x32, x64 — толстый клиент, тонкий клиент. 
  • Windows x32, x64 — толстый клиент, тонкий клиент, веб-клиент (Internet Explorer).

Примерный алгоритм использования

    • В 1С:Предприятие создается экземпляр компоненты
    • Во внешнее приложение/скрипт передаются порт прослушивания, ключ и ip адрес, если приложение расположено на другом сетевом хосте.
    • Из внешнего приложения/скрипта выполняются соответствующие веб-запросы.

 

Описание интерфейса компоненты

Создание экземпляра компоненты
Имя класса для создания объекта компоненты – WebExtEvent.

Пример:
КомпонентВнСоб = Новый («AddIn.MyComp.WebExtEvent»);
В процессе создания компоненты запускается web сервер, который слушает http запросы. Порт для прослушивания выбирается автоматически. После создания компоненты он доступен через свойство Порт. Также, во время создания экземпляра формируется случайный ключ, значение которого необходимо использовать как параметр при выполнении запросов. Ключ доступен через одноименное свойство.

Свойства

Порт – порт прослушивания веб-сервера. Формируется автоматически при создании экземпляра компоненты.
Ключ – строка, значение которой используется как параметр в веб запросах. Если значение параметра key в запросе и значение свойства Ключ не совпадают – соответствующая функция не будет выполнена. Может быть изменено в процессе работы.

Методы
Все методы возвращают результат в виде plain текста. Доступ к результатам можно получить через свойство Content.

ExternalEvent – Создает новое внешнее событие и помещает его в буфер.
Параметры
key – ключ, значение должно совпадать со значением свойства Ключ.
source –  источник события. Если параметр не указан, будет передана пустая строка.
message – сообщение. Если параметр не указан, будет передана пустая строка.
data – данные. Если папаметр не указан, будет передана пустая строка.
Возвращаемое значение
“true”, если запрос выполнен успешно, иначе – “false”.
Пример:
http://yourhost:yourport/ExternalEvent?key=yourkey&source=yoursource&message=yourmessage&data=yourdata

CleanEventBuffer – очищает буфер событий.
Параметры
key – ключ, значение должно совпадать со значением свойства Ключ.
Возвращаемое значение
“true”, если запрос выполнен успешно, иначе – “false”.
Пример:
http://yourhost:yourport/CleanEventBuffer?key=yourkey

GetEventBufferDepth – возвращает размер буфера сообщений
Параметры
key – ключ, значение должно совпадать со значением свойства Ключ.
Возвращаемое значение
Количество событий, которое может быть помещено в буфер событий. При ошибке возвращает 0.
Пример:
http://yourhost:yourport/GetEventBufferDepth?key=yourkey

SetEventBufferDepth – устанавливает размер буфера событий.
Параметры
key – ключ, значение должно совпадать со значением свойства Ключ.
depth – новый размер буфера событий. Строка, представляющая целое число.
Возвращаемое значение
“true”, если запрос выполнен успешно, иначе – “false”.
Пример:
http://yourhost:yourport/SetEventBufferDepth?key=yourkey&depth=yourdepth

22 Comments

  1. mdSerg

    Т.е. чтобы обратиться из внешнего приложения, надо сначала запустить 1С , которая вызовет эту компоненту?

    Не понял смысла компоненты — если надо запускать 1С?

    Что мешает напрямую обратиться к веб-сервису 1С?

    Приведите пример использования, пожалуйста.

    Reply
  2. Kabz

    Я думаю смысл использование дерганье тонкого ,веб клиента (формы) для передачи данных , с веб сервиса ты данные на форму просто так не передать.

    Reply
  3. blackhole321

    Kabz (2)

    Да, именно так.

    MdSerg (1)

    В качестве примера могу привести библиотеку «Звонки из Lync» http://infostart.ru/public/322875/. В ней взаимодействие с Lync реализовано на PowerShell. И все было бы хорошо, однако необходимо передавать информацию о звонке в клиент 1С:Предприятие. Можно использовать обработчик ожидания и периодически проверять наличие звонков, а можно при наступлении звонка вызывать ВнешнееСобытие, написав примерно следующий код:

    Invoke-WebRequest -Uri ‘http://localhost:yourport/ExternalEvent?key=yourkey&source=yoursource&message=yourmessage’ и обработать его в клиенте.

    В общем, область применения примерно такая же как и для внешних компонент с той лишь разницей, что это можно делать из внешнего скрипта/приложения.

    Reply
  4. mdSerg

    Т.е. взаимодействие непосредственно с клиентом 1С, а не с сервером? (это полезно)

    ВЕБ-Сервер д.б. установлен на клиентском компьютере?

    Reply
  5. blackhole321

    (4) mdSerg,

    Добрый день.

    Да, с клиентом 1С.

    Веб-сервер — это и есть внешняя компонента. Устанавливается на клиенте штатным образом.

    Reply
  6. dablack

    Спасибо автору, вроде то, что надо, но немного насторожил момент, что адрес и ключ присваиваются автоматически при запуске.

    А нет возможности их задать руками?

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

    И в случае с автоматическим назначением портов мобильному приложению придется перед каждым заданием на печать запрашивать текущий порт и ключ у веб-сервиса сервера 1С например (вдруг после прошлого задания на печать клиент 1с с запущенной ВК перезапустился….)

    А если порт всегда один и тот же, то я бы смог сразу из мобильного приложения отправлять запрос на нужный ip:port..

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

    Reply
  7. blackhole321

    (6) dablack,

    Ключ можно задать руками. Порт только для чтения т.к. он выбирается динамически из списка свободных tcp портов.

    Я правильно понимаю, что Вы хотите отправлять запросы на печать клиенту 1С на компьютере пользователя?

    Reply
  8. dablack

    Да, все верно, именно так.

    Reply
  9. blackhole321

    (8) dablack,

    Ну Вам все равно необходимо каким-то образом получать список доступных хостов, с которых можно производить печать. В таком случае можно при старте передавать информацию о хосте/порте/ключе на сервер 1С и периодически обновлять эту информацию. Мобильное устройство, считав информацию использует ее до момента возникновения ошибки, при попытке отправить запрос. При возникновении ошибки информация о доступных хостах/портах/ключах считывается заново.

    Reply
  10. dablack

    Все таки мне гораздо удобнее было бы работать со статическим портом.

    Файл скачал, но прошу прощения, а вы исходниками не поделитесь? Я думаю сам смогу разобраться даже со знаниями в С++ близкими к нулю где проставить порт. А то помимо печати есть еще мысли где я смогу применить данную компоненту и опять же порт очень хотелось бы статический… В любом случае спасибо!

    Reply
  11. dablack

    Насколько реально сделать так чтобы ExternalEvent возвращал не просто True/False а нечто другое (результат работы какой то либо функции) ?

    Reply
  12. blackhole321

    (11) dablack, Эта функция помещает событие в очередь, вызывая одноименную функцию платформы. Функция платформы возвращает тип Boolean, где возвращаемое значение — true, если событие добавлено в очередь и false, если произошла какая либо ошибка (см. описание интерфейса по ссылке в публикации). Соответственно получить какие-либо значения результатов вряд ли возможно. Для выполнения каких-либо действий на сервере 1С предприятие с последующим возвратом значений etc., Вы можете использовать web-сервисы.

    Если не секрет, чем вызвана необходимость вызывать исполннение кода с последующим возвратом результатов именно на клиенте?

    Reply
  13. dablack

    (12) То что в данный момент возвращает только boolean это я знаю и этим пользуюсь. Не секрет, вариантов очень много для чего есть необходимость такого прямого GET запроса и получения ответа. Например, складские работники перемещаются по складу с терминалами и сканируют шк адреса ячеек, шк непосредственно товара и в случае (как сейчас и есть) обращения к http сервисам базы, на каждый «пик» проходит аутентификация, инициализация модуля сеансов и т.д. а только после всего это возвращаются нужные данные. Т.е. достаточно большие накладные расходы. Если бы ваша компонента умела возвращать не только true/false то для меня бы это была отличная альтернатива — обращаться к постоянно «взведенному» веб серверу. Да конечно вроде в 8.3.9. реализован механизм повторного использования сессии, но попробовать на тесте еще не успел, да и не очень пока хочется продакш базу переводить на свежую платформу из-за пары фичь.

    p.s. очень не хватает все таки возможности задать порт руками) приходиться лишний http запрос делать на получение текущего порта…

    Reply
  14. blackhole321
    Reply
  15. soal

    Очень понравилась разработка. Стабильно работает на винде. Только не хватает задания конкретного порта для регистрации. Комп конечного пользователя за роутером, конкретный порт можно пробросить, а пробрасывать все порты на роутер — открыть широко дверь всем с инета — не айс.

    Reply
  16. blackhole321

    (15) Это некий компромисс, т.к. порт может быть занят другим приложением/сервисом. Как пример — Вы запустили несколько экземпляров клиента 1С. Такого рода проблему можно решить написав что-то типа reverse proxy. После загрузки компонента, происходит регистрация на proxy и отправка ID клиенту, соответственно наружу выставляется один порт. Клиент посылает запросы с указанием ID на один порт, прокси пересылает данные по нужному адресу/порту в соответствии с ID.

    Reply
  17. blackhole321

    (15) В качестве иллюстрации и отправной точки можете попробовать нижеследующий скрипт PowerShell:

    # Инициализируем web-сервер
    
    $ВебСлушатель = New-Object System.Net.HttpListener
    
    # По этому url регистрируем порт
    # Запрос на регистрацию: http://localhost:8080/НомерПорта
    $ВебСлушатель.Prefixes.Add(«http://localhost:8080/»)
    
    # По этому url принимаем внешние запросы
    # Необходимо запускать с правами администратора или настроить через netsh
    $ВебСлушатель.Prefixes.Add(«http://*:9090/»)
    
    $ВебСлушатель.AuthenticationSchemes = ‘Anonymous’
    $ВебСлушатель.Start()
    
    # Порт, который слушает клиент 1С
    $port = 0
    
    # Получаем и обрабатываем web-запросы
    
    while ($ВебСлушатель.IsListening)
    {
    $Контекст = $ВебСлушатель.GetContext()
    $UrlЗапроса = $Контекст.Request.Url
    $Ответ = $Контекст.Response
    
    $Результаты = «»
    
    if ($UrlЗапроса.Port -eq 8080)
    {
    # Регистрируем порт клиента
    $port = $UrlЗапроса.AbsolutePath.ToLower().Replace(«/», «»)
    
    }
    elseif ($UrlЗапроса.Port -eq 9090)
    {
    # Формируем строку запроса к клиенту 1С
    $UriЗапросаК1С = «http://localhost:» + $port + $UrlЗапроса.PathAndQuery
    
    # Выполняем запрос. Invoke-Webrequest не доступна в PowerShell 2
    $РезультатыЗапросаК1С = Invoke-WebRequest -Uri $UriЗапросаК1С
    
    $Результаты = $РезультатыЗапросаК1С.Content
    
    }
    
    # Возвращаем результат
    $Буфер = [System.Text.Encoding]::UTF8.GetBytes($Результаты)
    $Ответ.ContentLength64 = $Буфер.Length
    $Ответ.AppendHeader(«Content-Type»,»text;charset=utf-8″)
    $Ответ.OutputStream.Write($Буфер, 0, $Буфер.Length)
    $Ответ.Close()
    }
    
    

    Показать

    Он запускается на локальной машине, где работает клиент 1С. Запускать надо с правами администратора или разрешить слушать определенный порт обычному пользователю при помощи netsh

    Регистрация порта — отправка из 1С веб-запроса http://localhost:8080/НомерПорта

    На порт 9090 пробрасываете внешние запросы

    Поддерживается только один клиент.

    Reply
  18. soal

    (16) Порт может быть занят — вернуть ошибку «порт занят», или еще лучший вариант — если порт занят, запустить на свободном порту, а в коде можно будет проверить равен ли фактический порт заданному и уже решать в алгоритме, что дальше делать — отключаться или продолжать работу. В моем случае нужно будет отключиться и сообщить клиенту чтобы он проверил/освободил порт, либо пробросил новый порт (на который удалось подключиться) и указал его в настройках.

    (17) reverse proxy — это уже перебор. Клиент с ноутбуком сидит, через wi-fi роутер. Поднимать ему на ноутбуке еще прокси чтобы ретранслировать запросы самому на себя по динамическому порту — это уже похоже на извращения )

    Реально «предпочитаемый порт» добавит функциональной применимости Вашей компоненте в разы. Простой пример «проверь свои задачи». Вместо обработчика ожидания, который сейчас дергает мне сервер раз в минуту на предмет «а не появилось ли новых задач», можно было бы «по факту» оповестить клиента и «заставить его» открыть форму задачи сразу после ее записи в базе. Проблема с теми, кто сидит дома через веб-сервис. Сюда же еще проблема с тем, что работает только в MSIE, у нас большинство пользуют firefox и chrome, но это хотя бы решается установкой тонкого клиента.

    Reply
  19. blackhole321

    (18)

    Несомненно, отчасти Вы правы, но лишь отчасти. Ваш подход применим для частного случая, когда запущен один экземпляра клиента 1С.

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

    Относительно работы только в IE — нельзя об’ять необ’ятное :). Запустить веб-сервер в отдельном потоке внутри компоненты 1С, в браузере, чтобы работало стабильно, требует некоторых усилий 🙂 Скажем закрытие слушателя с последующим открытием на другом порту приводит к исключению, хотя в консольном приложении или обычной библиотеке все работает нормально.

    Так,что как я написал выше — это некий компромисс

    Reply
  20. soal

    (19) Запускать на заданном порту не при создании компоненты, а командой StarsServ(port), получающей порт в параметре, если порт занят (отсутствует в списке свободных портов), запустить на свободном порту, а в коде можно будет проверить равен ли фактический порт заданному и уже решать в алгоритме, что дальше делать — отключаться или продолжать работу» — никаких действий от пользователя не надо и сервер гарантированно стартонёт.

    Это тоже компромисс, который, тем не менее, повысит применимость компоненты. Тут же выше подобные вопросы уже были (10). Без задания приоритета при назначении порта — эта компонента только игрушка, а для создания стабильного рабочего решения бесполезна.

    Reply
  21. dablack

    Добрый день. Юрий, но может у вас получиться сделать возможность назначать порт при создании компоненты ? Реально рандомный номер порта очень мешает. Вариант предложенный в (20) всех устроит. Или поделитесь/продайте исходники мы сами поправим). Вариант с PowerShell не подходит.

    Спасибо.

    Reply
  22. blackhole321

    (21)Поищу исходники, но это все пробовалось 🙂 1С начинает валиться с ошибками, хотя вроде никаких предпосылок. Я поэтому и написал, что это компромиссный вариант

    Reply

Leave a Comment

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