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

0. Юрий Дешин (blackhole321) 73 02.02.15 13:47 Сейчас в теме
Простая внешняя компонента, которая позволяет генерировать внешние события в 1С:Предприятие из внешнего приложения/скрипта web запросом.

Перейти к публикации

Комментарии
1. Serg S (mdSerg) 02.02.15 17:54 Сейчас в теме
Т.е. чтобы обратиться из внешнего приложения, надо сначала запустить 1С , которая вызовет эту компоненту?
Не понял смысла компоненты - если надо запускать 1С?
Что мешает напрямую обратиться к веб-сервису 1С?
Приведите пример использования, пожалуйста.
2. kabz (Kabz) 74 03.02.15 00:13 Сейчас в теме
Я думаю смысл использование дерганье тонкого ,веб клиента (формы) для передачи данных , с веб сервиса ты данные на форму просто так не передать.
3. Юрий Дешин (blackhole321) 73 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) 73 09.11.15 12:34 Сейчас в теме
(4) mdSerg,
Добрый день.
Да, с клиентом 1С.
Веб-сервер - это и есть внешняя компонента. Устанавливается на клиенте штатным образом.
6. Sergey Mosalov (dablack) 19.05.16 00:01 Сейчас в теме
Спасибо автору, вроде то, что надо, но немного насторожил момент, что адрес и ключ присваиваются автоматически при запуске.
А нет возможности их задать руками?
Мне необходимо организовать, что то вроде сервера печати. С мобильных приложений поступает задание на печать документа с таким то идентификатором.
И в случае с автоматическим назначением портов мобильному приложению придется перед каждым заданием на печать запрашивать текущий порт и ключ у веб-сервиса сервера 1С например (вдруг после прошлого задания на печать клиент 1с с запущенной ВК перезапустился....)
А если порт всегда один и тот же, то я бы смог сразу из мобильного приложения отправлять запрос на нужный ip:port..
В любом случае после долгого безрезультатного поиска какого нибудь мини веб-сервера который работал сам по себе в виде службы и в который можно было бы отправлять запросы POST запросы с двоичными данными и чтобы он их отправлял на печать на нужный принтер, Ваш продукт очень даже пригодится. Спасибо!
7. Юрий Дешин (blackhole321) 73 19.05.16 16:49 Сейчас в теме
(6) dablack,
Ключ можно задать руками. Порт только для чтения т.к. он выбирается динамически из списка свободных tcp портов.
Я правильно понимаю, что Вы хотите отправлять запросы на печать клиенту 1С на компьютере пользователя?
8. Sergey Mosalov (dablack) 19.05.16 18:27 Сейчас в теме
Да, все верно, именно так.
9. Юрий Дешин (blackhole321) 73 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) 73 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) 73 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
Соответственно должны будут появиться результаты вызова соответствующих функций общего модуля.

Если устраивает производительность - добавляете многопоточность, передачу параметров и обработку ошибок
15. Alex Guess (soal) 12.10.17 12:29 Сейчас в теме
Очень понравилась разработка. Стабильно работает на винде. Только не хватает задания конкретного порта для регистрации. Комп конечного пользователя за роутером, конкретный порт можно пробросить, а пробрасывать все порты на роутер - открыть широко дверь всем с инета - не айс.
16. Юрий Дешин (blackhole321) 73 12.10.17 12:51 Сейчас в теме
(15) Это некий компромисс, т.к. порт может быть занят другим приложением/сервисом. Как пример - Вы запустили несколько экземпляров клиента 1С. Такого рода проблему можно решить написав что-то типа reverse proxy. После загрузки компонента, происходит регистрация на proxy и отправка ID клиенту, соответственно наружу выставляется один порт. Клиент посылает запросы с указанием ID на один порт, прокси пересылает данные по нужному адресу/порту в соответствии с ID.
18. Alex Guess (soal) 12.10.17 17:24 Сейчас в теме
(16) Порт может быть занят - вернуть ошибку "порт занят", или еще лучший вариант - если порт занят, запустить на свободном порту, а в коде можно будет проверить равен ли фактический порт заданному и уже решать в алгоритме, что дальше делать - отключаться или продолжать работу. В моем случае нужно будет отключиться и сообщить клиенту чтобы он проверил/освободил порт, либо пробросил новый порт (на который удалось подключиться) и указал его в настройках.
(17) reverse proxy - это уже перебор. Клиент с ноутбуком сидит, через wi-fi роутер. Поднимать ему на ноутбуке еще прокси чтобы ретранслировать запросы самому на себя по динамическому порту - это уже похоже на извращения )
Реально "предпочитаемый порт" добавит функциональной применимости Вашей компоненте в разы. Простой пример "проверь свои задачи". Вместо обработчика ожидания, который сейчас дергает мне сервер раз в минуту на предмет "а не появилось ли новых задач", можно было бы "по факту" оповестить клиента и "заставить его" открыть форму задачи сразу после ее записи в базе. Проблема с теми, кто сидит дома через веб-сервис. Сюда же еще проблема с тем, что работает только в MSIE, у нас большинство пользуют firefox и chrome, но это хотя бы решается установкой тонкого клиента.
19. Юрий Дешин (blackhole321) 73 12.10.17 21:40 Сейчас в теме
(18)
Несомненно, отчасти Вы правы, но лишь отчасти. Ваш подход применим для частного случая, когда запущен один экземпляра клиента 1С.
В случае если пользователь запустит несколько экземпляров или за роутером окажется несколько компьютеров, Ваш подход заставит пользователя совершить кучу действий. И хорошо, если пользователь достаточно квалифицирован, чтобы проделать все то, что Вы описали. И тут уже вопрос вкуса, что более правильно, заставлять пользователя иногда перенаправлять и сравнивать порты или один раз произвести настройку, скажем запуска скрипта при старте компьютера, пробросить порт на роутере и установить тонкого клиента.
Относительно работы только в IE - нельзя об'ять необ'ятное :). Запустить веб-сервер в отдельном потоке внутри компоненты 1С, в браузере, чтобы работало стабильно, требует некоторых усилий :) Скажем закрытие слушателя с последующим открытием на другом порту приводит к исключению, хотя в консольном приложении или обычной библиотеке все работает нормально.
Так,что как я написал выше - это некий компромисс
20. Alex Guess (soal) 13.10.17 12:57 Сейчас в теме
(19) Запускать на заданном порту не при создании компоненты, а командой StarsServ(port), получающей порт в параметре, если порт занят (отсутствует в списке свободных портов), запустить на свободном порту, а в коде можно будет проверить равен ли фактический порт заданному и уже решать в алгоритме, что дальше делать - отключаться или продолжать работу" - никаких действий от пользователя не надо и сервер гарантированно стартонёт.
Это тоже компромисс, который, тем не менее, повысит применимость компоненты. Тут же выше подобные вопросы уже были (10). Без задания приоритета при назначении порта - эта компонента только игрушка, а для создания стабильного рабочего решения бесполезна.
17. Юрий Дешин (blackhole321) 73 12.10.17 15:07 Сейчас в теме
(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 пробрасываете внешние запросы
Поддерживается только один клиент.
Оставьте свое сообщение