gifts2017

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

Опубликовал Юрий Дешин (blackhole321) в раздел Программирование - Внешние компоненты

Простая внешняя компонента, которая позволяет генерировать внешние события в 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

Скачать файлы

Наименование Файл Версия Размер
AddInNative.zip 17
.zip 389,70Kb
09.06.15
17
.zip 1.0.1 389,70Kb Скачать

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Serg S (mdSerg) 02.02.15 17:54
Т.е. чтобы обратиться из внешнего приложения, надо сначала запустить 1С , которая вызовет эту компоненту?
Не понял смысла компоненты - если надо запускать 1С?
Что мешает напрямую обратиться к веб-сервису 1С?
Приведите пример использования, пожалуйста.
2. kabz (Kabz) 03.02.15 00:13
Я думаю смысл использование дерганье тонкого ,веб клиента (формы) для передачи данных , с веб сервиса ты данные на форму просто так не передать.
3. Юрий Дешин (blackhole321) 03.02.15 09:23
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' и обработать его в клиенте.
В общем, область применения примерно такая же как и для внешних компонент с той лишь разницей, что это можно делать из внешнего скрипта/приложения.
4. Serg S (mdSerg) 08.11.15 21:01
Т.е. взаимодействие непосредственно с клиентом 1С, а не с сервером? (это полезно)
ВЕБ-Сервер д.б. установлен на клиентском компьютере?
5. Юрий Дешин (blackhole321) 09.11.15 12:34
(4) mdSerg,
Добрый день.
Да, с клиентом 1С.
Веб-сервер - это и есть внешняя компонента. Устанавливается на клиенте штатным образом.
6. Sergey Mosalov (dablack) 19.05.16 00:01
Спасибо автору, вроде то, что надо, но немного насторожил момент, что адрес и ключ присваиваются автоматически при запуске.
А нет возможности их задать руками?
Мне необходимо организовать, что то вроде сервера печати. С мобильных приложений поступает задание на печать документа с таким то идентификатором.
И в случае с автоматическим назначением портов мобильному приложению придется перед каждым заданием на печать запрашивать текущий порт и ключ у веб-сервиса сервера 1С например (вдруг после прошлого задания на печать клиент 1с с запущенной ВК перезапустился....)
А если порт всегда один и тот же, то я бы смог сразу из мобильного приложения отправлять запрос на нужный ip:port..
В любом случае после долгого безрезультатного поиска какого нибудь мини веб-сервера который работал сам по себе в виде службы и в который можно было бы отправлять запросы POST запросы с двоичными данными и чтобы он их отправлял на печать на нужный принтер, Ваш продукт очень даже пригодится. Спасибо!
7. Юрий Дешин (blackhole321) 19.05.16 16:49
(6) dablack,
Ключ можно задать руками. Порт только для чтения т.к. он выбирается динамически из списка свободных tcp портов.
Я правильно понимаю, что Вы хотите отправлять запросы на печать клиенту 1С на компьютере пользователя?
8. Sergey Mosalov (dablack) 19.05.16 18:27
Да, все верно, именно так.
9. Юрий Дешин (blackhole321) 19.05.16 18:47
(8) dablack,
Ну Вам все равно необходимо каким-то образом получать список доступных хостов, с которых можно производить печать. В таком случае можно при старте передавать информацию о хосте/порте/ключе на сервер 1С и периодически обновлять эту информацию. Мобильное устройство, считав информацию использует ее до момента возникновения ошибки, при попытке отправить запрос. При возникновении ошибки информация о доступных хостах/портах/ключах считывается заново.
10. Sergey Mosalov (dablack) 19.05.16 19:07
Все таки мне гораздо удобнее было бы работать со статическим портом.
Файл скачал, но прошу прощения, а вы исходниками не поделитесь? Я думаю сам смогу разобраться даже со знаниями в С++ близкими к нулю где проставить порт. А то помимо печати есть еще мысли где я смогу применить данную компоненту и опять же порт очень хотелось бы статический... В любом случае спасибо!
11. Sergey Mosalov (dablack) 04.10.16 20:58
Насколько реально сделать так чтобы ExternalEvent возвращал не просто True/False а нечто другое (результат работы какой то либо функции) ?
12. Юрий Дешин (blackhole321) 05.10.16 18:49
(11) dablack, Эта функция помещает событие в очередь, вызывая одноименную функцию платформы. Функция платформы возвращает тип Boolean, где возвращаемое значение - true, если событие добавлено в очередь и false, если произошла какая либо ошибка (см. описание интерфейса по ссылке в публикации). Соответственно получить какие-либо значения результатов вряд ли возможно. Для выполнения каких-либо действий на сервере 1С предприятие с последующим возвратом значений etc., Вы можете использовать web-сервисы.
Если не секрет, чем вызвана необходимость вызывать исполннение кода с последующим возвратом результатов именно на клиенте?
13. Sergey Mosalov (dablack) 10.10.16 20:56
(12) blackhole321, То что в данный момент возвращает только boolean это я знаю и этим пользуюсь. Не секрет, вариантов очень много для чего есть необходимость такого прямого GET запроса и получения ответа. Например, складские работники перемещаются по складу с терминалами и сканируют шк адреса ячеек, шк непосредственно товара и в случае (как сейчас и есть) обращения к http сервисам базы, на каждый "пик" проходит аутентификация, инициализация модуля сеансов и т.д. а только после всего это возвращаются нужные данные. Т.е. достаточно большие накладные расходы. Если бы ваша компонента умела возвращать не только true/false то для меня бы это была отличная альтернатива - обращаться к постоянно "взведенному" веб серверу. Да конечно вроде в 8.3.9. реализован механизм повторного использования сессии, но попробовать на тесте еще не успел, да и не очень пока хочется продакш базу переводить на свежую платформу из-за пары фичь.
p.s. очень не хватает все таки возможности задать порт руками) приходиться лишний http запрос делать на получение текущего порта...
14. Юрий Дешин (blackhole321) 11.10.16 21:24
(13) dablack, Ну если использование штатных http сервисов категорически не устраивает - остается написать их самостоятельно :)

1. Создайте насервере 1С тестовую информационную базу
2. Добавьте в нее общий модуль, с возможностью доступа из внешнего соединения
3. Добавьте в модуль следующий код:


Функция МойМетод1() Экспорт
	Возврат "Вызов метода МойМетод1";
КонецФункции

Функция МойМетод2() Экспорт
	Возврат "Вызов метода МойМетод2";
КонецФункции

Функция МойМетод3() Экспорт
	Возврат "Вызов метода МойМетод3";
КонецФункции
...Показать Скрыть


4. Зарегистрируйте COM компонент, если он не был зарегистрирован ранее
5. Если Вы используете 64 битную систему - создайте для COM объекта COM+ приложение

6. Создайте скрипт PowerShell примерно следующего содержания:

# Инициализируем подключение к 1С

$Коннектор1С = New-Object -ComObject V83.ComConnector

$СтрокаПодключения =  "srvr='ИмяМоегоСервера'; ref='ИмяМоейБазы';"
$Соединение1С = $Коннектор1С.Connect($СтрокаПодключения);
$МойОбщийМодуль = [System.__ComObject].InvokeMember("МойОбщийМодуль",[System.Reflection.BindingFlags]::GetProperty,$null,$Соединение1С,$null)

# Инициализируем web-сервер

$ВебСлушатель = New-Object System.Net.HttpListener

$ВебСлушатель.Prefixes.Add("http://localhost:8080/Method1/")
$ВебСлушатель.Prefixes.Add("http://localhost:8080/Method2/")
$ВебСлушатель.Prefixes.Add("http://localhost:8080/Method3/")

$ВебСлушатель.AuthenticationSchemes = 'Anonymous'    
$ВебСлушатель.Start() 

# Получаем и обрабатываем web-запросы

while ($ВебСлушатель.IsListening)
{
    $Контекст = $ВебСлушатель.GetContext()
	$UrlЗапроса = $Контекст.Request.Url
	$Ответ = $Контекст.Response
    
    $ИмяМетода1С = ""

    if ($UrlЗапроса.AbsoluteUri.EndsWith("Method1", [System.StringComparison]::InvariantCultureIgnoreCase))
    {
        $ИмяМетода1С = "МойМетод1" 
    }
    elseif ($UrlЗапроса.AbsoluteUri.EndsWith("Method2", [System.StringComparison]::InvariantCultureIgnoreCase))
    {
        $ИмяМетода1С = "МойМетод2"
    }
    elseif ($UrlЗапроса.AbsoluteUri.EndsWith("Method3", [System.StringComparison]::InvariantCultureIgnoreCase))
    {
        $ИмяМетода1С = "МойМетод3"
    }

    $Результаты = [System.__ComObject].InvokeMember($ИмяМетода1С,[System.Reflection.BindingFlags]::InvokeMethod,$null,$МойОбщийМодуль, $null)
    
    # Возвращаем результат
    $Буфер = [System.Text.Encoding]::UTF8.GetBytes($Результаты)
	$Ответ.ContentLength64 = $Буфер.Length
	$Ответ.AppendHeader("Content-Type","text;charset=utf-8")
	$Ответ.OutputStream.Write($Буфер, 0, $Буфер.Length)
    $Ответ.Close()
 }

...Показать Скрыть


7. Замените в строке подключения значения базы и сервера на свои. При необходимости добавьте имя пользователя и пароль.
8. Сохраните скрипт и запустите его.
9. В браузере в строку адреса введите:
http://localhost:8080/Method1
http://localhost:8080/Method2
http://localhost:8080/Method3
Соответственно должны будут появиться результаты вызова соответствующих функций общего модуля.

Если устраивает производительность - добавляете многопоточность, передачу параметров и обработку ошибок
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа