HTTP-сервисы в 1С Предприятие 8.3


В платформе 1С Предприятие 8.3 появилась возможность создавать HTTP-сервисы. Пример работы.

В платформе 1С Предприятие 8.3 появилась возможность создавать HTTP-сервисы

C помощью встроенного языка, теперь можно сформировать ответ на запрос. При этом у вас есть удобный доступ к телу, заголовкам и строке исходного запроса, а также имеется возможность формировать код, тело и заголовки ответа по своему усмотрению.

По сравнению с имеющимися в платформе SOAP web-сервисами, HTTP-сервисы имеют ряд преимуществ:

  • Простота программирования клиента таких сервисов;
  • Потенциально меньший объем передаваемых данных;
  • Потенциально меньшая вычислительная нагрузка;
  • HTTP-сервисы ориентированы на «ресурсы», в то время как SOAP сервисы ориентированы на «действия».

В базе http83.dt продемонстрирована реализация http-сервисов

http-сервис СписокНакладных

В http-сервисе использованы шаблоны URL, реализована обработка свойства ПараметрыURL объекта HTTPСервисЗапрос
В данном примере показано, как можно создать базу-прокладку между рабочей базой и корпоративным сайтом
Подключение производится к демо-базе "Управление торговлей 11", в которой предварительно для справочника "Контрагенты" нужно задать доп.свойство с названием Пароль к вэб, где будем хранить пароль для доступа.
В http-запрос будет передаваться в параметры URL: ИНН в качестве логина и пароль.
При обработке запроса происходит подключение через ComConnector к УТ(тормоза гарантированы Laughing ), и оттуда производиться выборка
Не претендую на быстроту и безопасность этого решения, оно в качестве примера

Итак. В дереве метаданных появилась новая ветка  — HTTP-сервисы
Создаем новый сервис, указываем его имя и корневой URL(list)
Корневой URL будет использоваться для вызова нашего http-сервиса
Далее добавим в http-сервис Шаблон URL, указав в качестве шаблона "/{Логин}/{Пароль}"
Такой шаблон позволит при обработке http-запроса получить в парметрах URL соответствующую структуру параметров и их значения
Теперь, к нашему шаблону URL добавим метод с именем "get", в качестве http-метода выберем GET
Открываем обработчик, и пишем код
Обработчик метода представляет собой функцию, которая должна возвращать значение типа HTTPСервисОтвет

http-сервис СтатусЗаказа

В примере реализована обработка переменных, переданных методом POST, формирование ответа в виде html-страницы.
Выборка данных на этот раз производится из базы, где лежат hs, поэтому работает существенно быстрее предыдущего http-сервиса.
При реализации рабочей системы имеет смысл создать объекты в базе (с сервисом) и настроить миграцию данных из базы-источника (например, отдельным фоновым процессом). Выборку при обработке http-запроса производить непосредственно из базы, где он находится.

Публикация

Об установке и настройке веб-сервера информации предостаточно.
Я использовал сборку httpd-2.2.25-win32-x86-openssl-0.9.8y.exe отсюда 
Устанавливал по методу "Далее-Далее-Финиш" 🙂
Публикация http-сервисов находится там же, где была и есть публикация веб-сервисов и особо ничем не отличается.
После установки веб-сервера в режиме "Конфигуратор" переходим по меню "Администрирование" — "Публикация на веб-сервере"
На вкладке "HTTP-сервисы" задаем имя публикации, веб-сервер , каталог публикации, и отмечаем наши сервисы(в моем случае имя — "web", веб-сервер Apache 2.2)
При публикации в файл конфигурации httpd.conf автоматически записываются соответствующие блоки и сервер перезапускается (при первой публикации)
Обращение http-сервису
Пример: http://mysite/web/hs/list , где
mysite — адрес сервера (на если веб-сервер установлен локально — можно использовать 127.0.0.1)
web — имя, указанное при публикации (альяс)
hs — обязательный сегмент пути, сообщающий серверу, что будет происходить работа с http-сервисами
list — корневой url веб-сервиса

Тестируем

Список накладных

http://127.0.0.1/web/hs/list/7705260681/pswMP (Не забудьте настроить доп.свойство в УТ для пароля)

Предполагается, что для доступа к реестру документов пользователь обращается по прямой ссылке, содержащей ИНН и пароль

Статус заказа

http://127.0.0.1/web/hs/check

Запрос и ответ расположены по одному URL. При входе на страницу срабатывает метод GET, возвращающий html-форму 

При нажатии "Проверить" происходит отправка номера заказа методом POST на этот же URL, ответ возвращается с той же формой запроса, дополненной данными по запрошенному заказу. 

 

В прикрепленном файле — выгрузка БД для 1С 8.3. В конфигурации реализованы 2 http-сервиса (из публикации), настройка com-соединения с демао базой УТ 11, документ "Заказ".

 

Что потребуется для запуска и тестирования

  • веб-сервер
  • любой веб-браузер
  • актуальный релиз 1С: Предприятие 8.3 

58 Comments

  1. dmitry-gr

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

    Reply
  2. AllexSoft

    Вообще нужная статейка, всегда интересно посмотреть на новые возможности…

    (1) dmitry-gr, зачем?

    Reply
  3. awk

    (0) Статья: «Зырь какие картинки. Дай стартмань качни поставь и разбирайся»?

    Reply
  4. vano-ekt

    (3) в формате «ребятам о зверятах» или «1с для чайников» хочется? Статья для программистов, предполагается что есть опыт работы с хотя бы ws. Тонны теории по протоколу http, куча картинок и многабукф — кому это надо, тут не вики 🙂

    В выходные запилю поинтересней статью, букв будет немного, но конфа посерьезней

    Reply
  5. molodoi1sneg

    (4) спасибо за публикацию. Сходу не взлетело, буду разбираться дальше.

    Reply
  6. vano-ekt

    (5) а что не получилось?

    Reply
  7. sergei2k

    Для нормальной работы, например в Хроме, нужно установить заголовок Content-Type = text/html

    Reply
  8. vano-ekt

    (7) если html страничка — то да, в базе так и сделано

    Функция СтатусЗаказа(Запрос) Экспорт
    
    Ответ = Новый HTTPСервисОтвет(200);
    
    Ответ.Заголовки.Вставить(«Content-Type»,»text/html; charset=windows-1251″);
    
    тОтвет = «<h2>Проверить состояние заказа</h2>»;
    ПараметрыЗ = ОбщегоНазначения.ПолучитьПараметры(Запрос.ПолучитьТелоКакСтроку());
    
    ФормаСтатуса = ПолучитьОбщийМакет(«ФормаСтатусаЗаказа»).ПолучитьТекст();
    тОтвет = тОтвет+ФормаСтатуса;
    
    НомерЗаказа  = ПараметрыЗ[«OrderNum»];
    Если ЗначениеЗаполнено(НомерЗаказа) Тогда
    Попытка
    НомерЗаказа = Формат(Число(НомерЗаказа),»ЧЦ=9; ЧВН=; ЧГ=»);
    Исключение
    КонецПопытки;
    ЗаказСсылка = Документы.Заказы.НайтиПоНомеру(НомерЗаказа);
    Если ЗначениеЗаполнено(ЗаказСсылка) Тогда
    тОтвет = тОтвет+ «Заказ «+ НомерЗаказа +»(» + ЗаказСсылка.Сумма +» р.): » +  ЗаказСсылка.Состояние +»«;
    Иначе
    тОтвет = тОтвет+ «Заказ «+ НомерЗаказа +» не найден«;
    КонецЕсли;
    КонецЕсли;
    
    Ответ.УстановитьТелоИзСтроки(«<html><head><title>Проверить статус заказа</title></head><body>»+тОтвет+»</body></html>»);
    
    Возврат Ответ;
    
    КонецФункции

    Показать

    Reply
  9. DitriX

    (8) представляю себе, если бы я так статьи писал 🙂

    Я тоже вот собирался на днях немного заняться этим вопросом более детально. Но раз вы уже начали, то продолжайте, только копайте глубже.

    Если нет опыта в написании статей такого рода -могу предложить свою помощь, на уровне критика и консультанта.

    Reply
  10. dj_serega

    HTTP наше все 🙂

    Reply
  11. vano-ekt

    (9) я думаю, лучше свою напишите, больше публикаций — больше информации 🙂 тем более механизм новый, а на ИС пока только эта статья

    Reply
  12. vano-ekt

    добавил, как обещал http://infostart.ru/public/303477/

    Reply
  13. otcheskiy

    орддол

    Reply
  14. otcheskiy

    Опубликовал Web-сервис и HTTP-сервис на Демонстрационная конфигурация «Управляемое приложение» (1.0.18.4)

    Web-сервис работает нормально.

    Но при открытии HTTP-сервиса запрашивает логин/пароль.

    IIS 8.5

    На публикуемом приложении настроена анонимная проверка подлинности.

    Не могу понять от куда надо брать комбинацию логин/пароль ио ОС или из 1С

    Reply
  15. GreenDragon

    (2) AllexSoft, Чтобы костыли вовремя менять на платформенные решения

    Reply
  16. AllexSoft

    (15) GreenDragon, а чем тебя стандартные ответы SOAP не устроили ? ну разве что не все совместимо с некоторыми поделками от MS (насколько я слышал)… хз, уже сколько работаю с Web-сервисами.. проблем особых не возникало

    Reply
  17. GreenDragon

    (17) AllexSoft, вот как раз не меня не устраивает, а php-программеров. Вместо громоздкого SOAP, стройненький POST/GET. Т.е. встраивание в уже работающий вэб-ресурс 1С-ки пройдёт гораздо быстрее и проще.

    Reply
  18. AllexSoft

    (18) GreenDragon, нуну.. )) во первых по нагрузкам что?) не думаю что по производительности это выйдет быстрее чем тот же SOAP. А по лицензиям что? как выясняется в отличии от Web-сервисов которым нужно всего 1 лицензию по сути — для HTTP-сервера так не прокатит. Так что про встраивание «в уже работающий вэб-ресурс 1С-ки» — это вы загнули

    Reply
  19. GreenDragon

    (19) AllexSoft, это с каких это умозаключений для реализации http запросов от сайта не будет достаточно по сути одной лицензии? По производительности… Ну, смотри… Мне нужно получать с сайта запрос на остаток товара в базе и отправлять в ответ количество. Сдаётся мне, что получить 1 параметр и отправить обратно другой всяко будет шустрее, чем принимать, парсить, отправлять в ответ soap пакеты. И сколько нужно кода накатать php-шнику, чтобы сформировать SOAP пакет, а потом ответный отпарсить? Не нужно забивать гвозди микроскопом, а детскую песочницу наполнять с помощью карьерного самосвала. Для каждой цели нужен свой инструмент. Вот нам и дали ещё один такой.

    Reply
  20. AllexSoft

    (20) GreenDragon, в обсуждениях видел что на каждый сеанс нужна лицензия и он не отпускает ее пока сеанс не завершен (то есть пока юзер не закроет страницу) — хотя чувствуется мне что можно придумать костыль который это будет обходить.

    Сдаётся мне, что получить 1 параметр и отправить обратно другой всяко будет шустрее, чем принимать, парсить, отправлять в ответ soap пакеты. И сколько нужно кода накатать php-шнику, чтобы сформировать SOAP пакет, а потом ответный отпарсить?

    все это так конечно, быстрее в плане обработки postget стандартный чем SOAP, но в реальных условиях SOAP используется для синхронизации данных, ну скажем есть табличка на сайте с прайсом которая обновляется из 1С через SOAP, раз в час скажем.. это небольшая нагрузка для 1С. А что будет если вы замените этот механизм запросом цен на postget от 1С в реальном времени.. а если у вас тысячи посетителей на портале? Правильно.. все будет так тормозить что врагу не пожелаешь.. а табличку с ценами удобнее все же через SOAP передавать, с точки зрения PHP-прогера это 2 строки подключения SoapClient и небольшой цикл просто по строкам ответа (там обычный массив многомерный).. и ничего парсить не надо.. так же есть библеотеки на PHP которые позволяют сразу работать с ответами SOAP через DOM, с ними еще проще )

    Кароче HTTP-сервисы на 1С это штука конечно хорошая и нужная, вот только на чем то более менее серьезном не применимая, а для какого-нибудь внутрикорпоративного портала небольшой организации или организации скажем какого то кусочка личного кабинета пользователей (ну скажем моя корзина заказов) может и подойдет.. хотя я тут бы в сторону REST-сервисов от 1С посмотрел бы )

    Reply
  21. vano-ekt

    (21) да почему внутрикорпоративная… понятно, что интернет-магазин никто писать не будет на 1С 🙂

    но вот разрабам, занимающимся интеграцией 1С<->web, жизнь очень даже упрощается. Генерация контента — как в моей соседней ветке — это чистой воды провокация 🙂 Но вот, скажем, обновление таблиц цен/остатков, или там уведомление о новом заказе, платеже — вполне адекватный механизм. Яндекс.деньги кстати имеют тоже инструмент http-уведомлений о новых платежах, и отправляют они их на URL, дак вот, зачем яндексу отправлять это уведомление на сайт(и зачем вообще держать сайт для уведомлений), когда можно его послать сразу в 1С?

    Reply
  22. AllexSoft

    (22) я сам занимаюсь интеграцией web-1C.. и не сказал бы

    жизнь очень даже упрощается

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

    Для каждой цели нужен свой инструмент. Вот нам и дали ещё один такой.

    ПС:

    Генерация контента — как в моей соседней ветке — это чистой воды провокация 🙂

    а мне понравилось) жду продолжения

    Reply
  23. GreenDragon

    (21) AllexSoft,

    GreenDragon, в обсуждениях видел что на каждый сеанс нужна лицензия и он не отпускает ее пока сеанс не завершен

    Сеанс с кем?

    Reply
  24. DoctorRoza

    Спасибо, информация нужная! 🙂

    Reply
  25. Bacemo

    Информация кстати. Почитал.

    Reply
  26. nipil

    (8)

    Простите, я правильно понимаю?

    Должно быть:

    Функция СтатусЗаказа(Запрос) Экспорт

    Ответ = Новый HTTPСервисОтвет(200);

    Ответ.Заголовки.Вставить(«Content-Type»….. и.т.д.

    Reply
  27. ДимокШ

    Подскажите, возможно кто-то сталкивался:

    Создать в базе http-сервис, который должен по переданному URL находить в базе документ и записывать в этот документ комментарий.

    http сервис отрабатывает, документ находится, но находясь в модуле HTTP сервиса — получаю ошибку при попытке получить объект документа, например ПКОссылка.ПолучитьОбъект()

    т.е. метод ПолучитьОбъект() в этом модуле недоступен… как это можно победить?

    Reply
  28. vano-ekt

    (28) ни кода, ни текста ошибки …

    Reply
  29. ДимокШ

    в любой процедуре обработчике http сервиса если добавить строку

    об = Документы.Событие.СоздатьДокумент();

    то браузер выдаст ошибку:

    Ошибка инициализации модуля: Документ.ПКО

    Reply
  30. mzelensky

    Интересные материалы. Автору +

    Reply
  31. dour-dead

    Долго думал, из-за чего возникает ошибка )))

    «Неверная сигнатура процедуры обработчика»

    Оказывается нужен параметр в функцию обработчика.

    Функция ЛюбойURLФорма(Запрос)
    
    Возврат Ответы.ПолучитьОтветФормуСтатусЗаказа(Запрос);
    
    КонецФункции
    Reply
  32. Black Romeo

    Статья очень интересная, автору +

    При попытке поднять у себя HTTP сервис, почему-то не работает.

    В виртуальной машине 2008r2, web server IIS, платформа 8.3.5.1383

    База клиент-серверная, сервер приложения 32 битный.

    При подключении по строке http://localhost/web/hs/list/login/password как на скриншоте.

    Логика приложения чуть чуть другая, но тоже два параметра в URL ссылке.

    Кстати, а можно ли как-нибудь отладить http-сервис? я проставил везде код записи в журнал регистрации, но там пусто…

    Reply
  33. vano-ekt

    (33) для отладки укажите в файле default.vrd

    <debug enable=»true» url=»tcp://localhost»/>

    перед закрывающим тегом </point>

    ну и в конфигураторе в настройках подключения отладчика поставьте флаг «http-сервисы»

    потом рестарт веб-сервер

    Reply
  34. vano-ekt

    (33) относительно ошибки — какой-то косяк с публикацией, с IIS не запускал, поэтому по публикации не подскажу,

    апаче ближе и внятней (для меня) 🙂

    покурите мануалы тут http://its.1c.ru/db/v83doc#bookmark:adm:TI000000248

    Reply
  35. Black Romeo

    + (33) разобрался со своей ошибкой

    1) Создал на сервере пользователя WEB с паролем, с группами по умолчанию («Пользователи»);

    2) На сервере IIS, в «Проверке подлинности», в удостоверение пользователя анонимного доступа прописываем пользо

    Reply
  36. bpc222

    (14) otcheskiy,

    в настройках приложения на IIS (настройка сайта или приложения) укажите от имени какого пользователя ОС будет производиться аутентификация при анонимном обращении. Этого же пользователя ОС свяжите с пользователем в ИБ 1С.

    Reply
  37. vlad.frost

    (14), (37), возможен вариант авторизации и без привязки пользователя ОС в ИБ. Достаточно в файле default.vrd указать реквизиты в пути к информационной базе в атрибуте элемента point:

    <point xmlns=»http://v8.1c.ru/8.2/virtual-resource-system»

    xmlns:xs=»http://www.w3.org/2001/XMLSchema»

    xmlns:xsi=»http://www.w3.org/2001/XMLSchema-instance»

    base=»/_виртуальный_каталог_»

    ib=»File="C:\_полный_путь_к_информационной_базе_";Usr="_имя_пользователя_";Pwd="_пароль_пользователя_"»>

    Reply
  38. Pashkaa

    (38) Наткнулся еще на вот такой способ раздельной авторизации http://profi1c.ru/products/fba_toolkit/different_authentication_web_service­.html

    Но вот вопрос возможна ли раздельная авторизация при использовании http-сервисов («вход без учетки») и Веб клиентов (вход под учетками 1С) остается открытым.

    Reply
  39. vlad.frost

    (39) В вашем случае достаточно применить небольшую смекалку и сделать отдельные виртуальные каталоги для веб-клиента и для http-службы, каждый со своим файлом default.vrd со своими настройками авторизации.

    Reply
  40. alfaservice

    Вопрос! здесь представлен вариант подключения к другой базе и вывод информации. А если провернуть в одной базе как быть?

    Reply
  41. ture

    https?

    Как много смысла в столь короткой фразе!

    Reply
  42. TitanLuchs

    (38) vlad.frost, спасибо, работает. Только имя и пароль должны быть без кавычек, иначе вылетает ошибка разбора XML.

    Reply
  43. Чарик

    хорошая статья! были бы еще примеры кода — цены бы ей не было!

    Reply
  44. Wi5hMaCTeP

    Добрый день!

    Начал разбираться с темой и наткнулся на грабли:

    Есть 2 конфигурации — клиент (мобильное приложение) и сервер (просто чистая конфигурация).

    Создал на сервере HTTP сервис, настроил, опубликовал, проверил — все чудесно!

    Далее добавил пользователя, создал ему роль с полными правами (дал доступ на метод сервиса) и вот тут пошла трабла — ошибка 403 нет доступа.

    Подробнее:

    <!DOCTYPE HTML PUBLIC «-//IETF//DTD HTML 2.0//EN»>
    <html><head>
    <title>403 Forbidden</title>
    </head><body>
    <h1>Forbidden</h1>
    <p>You don’t have permission to access /http://127.0.0.1/CBTestConf/hs/Service/TestURL/Method on this server.</p>
    </body></html>

    Не могу разобраться в чем дело, вот собственно вся процедура вызова из клиента:

    Функция ОбменПоHTTPСервисуНаСервере()
    
    Пользователь = «Admin»;
    Пароль    = «123»;
    
    Попытка
    Соединение = Новый HTTPСоединение(«127.0.0.1», 80, Пользователь, Пароль);
    Исключение
    Возврат «Ошибка при создании HTTP соединения.»;
    КонецПопытки;
    
    ПараметрЗапроса = «1»;
    
    HTTPЗапрос = Новый HTTPЗапрос(«http://127.0.0.1/CBTestConf/hs/Service/TestURL/Method»);
    HTTPЗапрос.УстановитьТелоИзСтроки(ПараметрЗапроса);
    
    Ответ = «»;
    
    Попытка
    HTTPОтвет = Соединение.Записать(HTTPЗапрос);
    Ответ     = HTTPОтвет.ПолучитьТелоКакСтроку();
    Исключение
    Возврат «Ошибка при соединении.»;
    КонецПопытки;
    
    Возврат Ответ;
    КонецФункции

    Показать

    Конфигурацию уже несколько раз публиковал, Apache 2.2 перезапускал, права повсюду проверял, default.vrd правил (добавлял туда логин с паролем), в общем не знаю куда дальше головой биться 🙁

    В этих же конфигурациях настроен Web сервис, работает все прекрасно. Тоже была проблема после создания пользователя, но решалась дополнительным вводом пароля для WSПрокси.

    Тут же нет ничего кроме ИнтернетПрокси, но это отношения к делу не имеет.

    Очень прошу помочь!

    Заранее спасибо 🙂

    Reply
  45. rozer

    спасибо за публикацию!

    Reply
  46. Wi5hMaCTeP

    Прошу прощения за дурацкий вопрос — сам разобрался 🙂

    В HTTPЗапрос не надо было вставлять адрес сервера, только строку запроса 🙂

    Reply
  47. tindir

    отличная статья. В приложении много полезной инфы. Но возник вопрос в ходе своей разработки http-сервисов — каким образом можно реализовать механизм авторизации пользователя? Задним мозгом доходит что необходимо выкинуть сервис по авторизации и если условия авторизации(пароль,логин, ключ) выполнены то выдавать токен генерируемый самой Эской. Но как быть если в разрабатываемый фрон для работы с сервисами из нескольких баз? Скорее всего необходимо реализовать свой сервер авторизации гнать все вызовы с его проверкой? или можно как-то унифицированно работать с этим делом. Задача стоит следующая: веб-АРМ с отчетами для руководителя из нескольких баз (УТ,ЗУП,БП).

    Reply
  48. Zhilyakovdr

    (39) Pashkaa, Добрый день!

    1С «съедает» basic авторизацию например при обращении из jQuery:

    $.ajax({

    crossDomain: true,

    type: «GET»,

    contentType: «application/json;charset=utf-8»,

    url: «http://localhost/base/hs/test/Users»,

    dataType: «json»,

    beforeSend: function(xhr) {

    xhr.setRequestHeader(‘Authorization’, «Basic » + window.btoa(‘User’, ‘Pass’));

    },

    success: function (queryResult) {

    $(«#test»).text(queryResult.test);

    },

    error: function (xhr, ajaxOptions, thrownError) {

    $(«#test»).text(«ХХХ»);

    }

    });

    Reply
  49. kiruha

    А есть инфа как подключать оптимально таблицы стилей и картинки ?

    Как то можно по простому — через каталог на вебсервере ? (про Base64 видел способ). Кидаю файлы картинок в каталог публикации — не видны

    Reply
  50. alprk

    (42) можно хоть в апаче настроить, хоть другой сервер на фронт поставить

    Reply
  51. alprk

    А пароль в GET-запросе конечно жесть 🙁

    Reply
  52. vano-ekt

    (52) сделай форму пост в энтерпайс-решении (хотя и post как мертвому припарка)

    это тупой пример в лоб, болванка «для посмотреть» на hs в 1С 3х-летней давности

    Reply
  53. alprk

    (53)

    (52) сделай форму пост в энтерпайс-решении (хотя и post как мертвому припарка)

    Ну это был бы геморрой но можно и Basic же оставить, для этого вообще ничего не надо делать)

    Reply
  54. vano-ekt

    (54) да тут не про безопасность же, а о том что через ПараметрыURL можно передать какие-либо аргументы в hs

    Reply
  55. 7OH

    (40) а не подскажете — как это сделать правильно ?

    Тоже интерсено — как сделать на разные httpsws сервисы — разную авторизацию

    Reply
  56. BigBoss

    При обработке запроса происходит подключение через ComConnector к УТ

    Т.е. при подключении будет расходоваться лицензия?

    Reply
  57. vano-ekt

    (57) через com — это просто пример (привет из 2014 года) , как без изменения конфы можно получить доступ из самодельной базы к данным типовой конфы.

    Сегодня (в 2017) можно http-сервис реализовать в расширении конфигурации, той же УТ. Про лицензии надо читать, за 3 года где только не обсуждалось, и тут, и на мисте

    Reply

Leave a Comment

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