Добрый день, коллеги!
Мне не приходилось ранее работать с http-сервисами, но поставили задачу, про которую чуть ниже, и на мой взгляд http-сервисы -это самый простой путь к ее реализации. Потратив несколько часов на "гугление" и разбирательства, был написан этот http-сервис. И чтобы потом при необходимости не вспоминать как это все работает, решил написать статью в виде шпаргалки, чтобы всегда можно было вспомнить. Может кому тоже пригодится.
Небольшое отступление от темы:
В этой статье НЕ будет рассказано, про установку и настройку веб-сервера и подробности работы с http-сервисами в 1С Предприятие 8. Это все можно узнать прочитав статьи, ссылки на которые указаны ниже.
Благодарности:
Выражаю благодарность Илье Головацкому (YAGolova) и Дмитрию Сидоренко (dsdred) за статьи по этой теме. Результат был получен гораздо быстрее, чем если бы пришлось разбираться самостоятельно. Вот ссылки на статьи, которые использовались при изучении вопроса:
HTTP-сервисы для тех, кто ничего не понимает в WEB
HTTP Сервисы: Путь к своему сервису. Часть 1 Это серия из 4-х статей. Привожу ссылку на первую статью, ссылки на остальные можно взять уже там.
Рассказывать постараюсь кратко. Итак приступим.
Задача: Большой холдинг. Занимается производством.. неважно чего :) Не все непосредственно работают в программе 1С, но большинству нужна информация по производимой номенклатуре, с ее разнообразными параметрами. Поэтому была поставлена задача: просмотр списка номенклатуры и карточки выбранной номенклатуры в интернет браузере.
После поиска информации по теме было принято решение использовать http-сервисы в платформе 1С 8.3
Разработка и тестирование производилось на платформе 1С Предприятие 8.3 (8.3.13.1513) в серверном режиме. СУБД - MSSQL 2008
В качестве Web-Сервера использовался Apache 2.2.25 под Windows. Пример настройки например здесь: Настройка веб сервера Apache + 1С (Пошаговое руководство)
Apache настроен на локальной машине, доступен по адресу: http://localhost
Работа сервиса протестирована в Internet Explorer, Google Chrome, Yandex браузере.
Логика работы:
Создаем HTTP -Сервис, который выполняет две функции:
- возвращает список номенклатуры
- возвращает данные выбранной номенклатуры по коду
Важно: В нашем примере данные в браузер передаются уже в виде готового текста формата html и браузер отображает полученный html-код .
Функции, которые обрабатывают запрос, формируют табличный документ с данными, который сохраняется в формате html, и полученный текст html передается уже в качестве ответа в браузер.
Реализация:
1. Создаем новую базу на сервере 1С Предприятие.
2. В ветке метаданных "Общие" находим "HTTP-сервисы" и в нем Добавляем новый сервис. Я назвал его "ПросмотрНоменклатурыЧерезБраузер". Здесь же определяем очень важное свойство "Корневой URL", ставим значение "Nom" - используется как часть будущей ссылки на наш сервис.
На вкладке "Шаблоны URL" создаем шаблон "Номенклатура" и устанавливаем значение /{Metod}
В параметр {Metod} мы будем передавать название метода в наш http-сервис из браузера. И соответственно по названию будем определять, что нужно сделать. Больше обязательных данных нет. Поэтому в шаблоне больше ничего не указываем.
Нам еще нужен дополнительный параметр "Code", который нужно будет передать на наш сервис для получения данных по конкретной номенклатуре (простой поиск по коду). Его мы будем будем передавать в браузере в следующем виде: http://localhost/[ИмяБазы]/hs/Nom/GetDataNom?Code=[ЗначениеКода] . При обработке запроса найдем в нем значение параметра "Code".
Далее добавим к ранее созданному шаблону метод "Получить", который имеет тип HTTP-метод "GET".
Далее создадим обработчик метода "ОбработатьЗапрос" и перейдем в модуль нашего HTTP-сервиса.
В нем вызывается экспортная функция общего модуля, о котором подробно рассказано далее. Код обработчика:
// Функция метода GET
Функция ОбработатьЗапрос(Запрос)
Ответ = Service_http_ОбработкаМетодовGet.ОбработатьЗапросНаСервере(Запрос);
Возврат Ответ;
КонецФункции // ОбработатьЗапрос()
Для удобства разработки и отладки лучше вынести организацию нужного функционала в общий модуль. Сделаем это.
Добавим общий модуль "Service_http_ОбработкаМетодовGet" и реализуем экспортную функцию первичной обработки запроса, которая будет вызываться из обработчика "ОбработатьЗапрос".
// ОСНОВНАЯ ФУНКЦИЯ ДЛЯ ВЫЗОВА ИЗ ОБРАБОТЧИКА HTTP-СЕРВИСА
Функция ОбработатьЗапросНаСервере(Запрос) Экспорт
Ответ = Новый HTTPСервисОтвет(200);
ИмяМетода = запрос.ПараметрыURL.Получить("Metod"); // Возвращает список номенклатуры
// В ЗАВИСИМОСТИ ОТ МЕТОДА ПОЛУЧЕННОГО ИЗ ВХОДЯЩЕГО ЗАПРОСА СФОРМИРУЕМ ОТВЕТ
Если ИмяМетода = "GetListNom" Тогда
ТабДокХТМЛ = СформироватьСписокНоменклатурыНаСКД();
ИначеЕсли ИмяМетода = "GetDataNom" Тогда // Возвращает данные выбранной номенклатуры
КодНоменклатуры = Запрос.ПараметрыЗапроса.Получить("Code");
Если КодНоменклатуры <> Неопределено Тогда // если параметр передан
ТабДокХтмл = СформироватьДанныеПоНоменклатуре(КодНоменклатуры);
Иначе
Ответ.КодСостояния=405;
ТабДокХТМЛ = "Отсутствует значение кода номенклатуры ";
КонецЕсли;
Иначе
Ответ.КодСостояния=405;
ТабДокХТМЛ = "Отсутствует метод " + ИмяМетода;
КонецЕсли;
Ответ.УстановитьТелоИзСтроки(ТабДокХТМЛ,КодировкаТекста.UTF8);
// ЧТОБЫ НА СТРАНИЦЕ КОРРЕКТНО ОТОБРАЖАЛАСЬ КОДИРОВКА ДОБАВИМ СЛЕДУЮЩИЙ КОД
Ответ.Заголовки.Вставить("Content-Type","text/html; charset=utf-8");
Возврат Ответ;
КонецФункции // ОбработатьЗапросНаСервере()
Получение списка номенклатуры сделано с помощью общего макета на СКД, чтобы при необходимости можно было быстро изменить внешний вид списка. Для Этого создаем Общий макет. У меня он называется "МакетСКД", тип "Схема компоновки данных". Добавляем Набор данных "Запрос". Текст запроса очень простой, но там есть поле, которое служит ссылкой для перехода в браузере на получение данных по конкретной номенклатуре. Текст запроса следующий:
ВЫБРАТЬ
Номенклатура.Родитель,
Номенклатура.Код,
Номенклатура.Артикул,
"<a " + "href=""" + "http://localhost/ddashevsky_http/hs/Nom/GetDataNom?Code=" + Номенклатура.Код + """" + ">" + Номенклатура.Наименование + "</a>" КАК Перейти,
Номенклатура.Представление
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура.ЭтоГруппа
Здесь поле "Перейти" формируется в виде кода на html, которую браузер отображает как ссылку с вызовом метода GetDataNom, который уже служит для получения данных выбранного элемента справочника номенклатуры.
В остальном все стандартно.
Скриншот настроек макета:
Текст функции "СформироватьСписокНоменклатурыНаСКД"
Функция СформироватьСписокНоменклатурыНаСКД()Экспорт
ТабДок = Новый ТабличныйДокумент;
СхемаКомпоновкиДанных= ПолучитьОбщийМакет("МакетСКД");
КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных();
КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных));
КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
НастройкиОтчета = КомпоновщикНастроек.ПолучитьНастройки();
// ФОРМИРУЕМ МАКЕТ, С ПОМОЩЬЮ КОМПОНОВЩИКА МАКЕТА
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
// ПЕРЕДАЕМ В МАКЕТ КОМПОНОВКИ СХЕМУ, НАСТРОЙКИ, ДАННЫЕ РАСШИФРОВКИ
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных,НастройкиОтчета,ДанныеРасшифровки);
// ВЫПОЛНЯЕМ КОМПОНОВКУ С ПОМОЩЬЮ ПРОЫЕССОРА КОМПОНОВКИ
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки,,ДанныеРасшифровки,Истина);
// ВЫВОДИМ РЕЗУЛЬТАТ В ТАБЛИЧНЫЙ ДОКУМЕНТ
ПроцессорВывода =новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
ТабДок.ОтображатьЗаголовки = Ложь;
ТабДок.ОтображатьСетку = Ложь;
// СФОРМИРУЕМ HTML-КОД ИЗ ТАБЛИЧНОГО ДОКУМЕНТА
ТекстХТМЛ = СформироватьХТМЛИзТабдок(ТабДок);
Возврат ТекстХТМЛ;
КонецФункции // СформироватьСписокНоменклатурыНаСКД()
Здесь тоже все стандартно. Получаем общий макет. Инициализируем, выполняем, выводим результат в табличный документ.
После чего вызываем функцию формирования HTML-кода из табличного документа.
// ВОЗВРАЩАЕТ HTML-КОД ТАБЛИЧНОГО ДОКУМЕНТА
Функция СформироватьХТМЛИзТабдок(ТабДок)
ИмяВремФайла = ПолучитьИмяВременногоФайла();
//имяВремФайла = "E:\temp\ном.html"; // временно для отладки
ТабДок.Записать(ИмяВремФайла,ТипФайлаТабличногоДокумента.HTML);
ТекстХТМЛ = Новый ТекстовыйДокумент;
ТекстХТМЛ.Прочитать(ИмяВремФайла);
ТекстХТМЛ = ТекстХТМЛ.ПолучитьТекст();
// КАК ВЫЯСНИЛОСЬ ПРИ СОХРАНЕНИИ В ФОРМАТ HTML ТЕГИ НЕ ПОКАЗЫВАЮТСЯ В ОКНЕ БРАУЗЕРА, ПОЭТОМУ ОНИ ЗАМЕНЯЮТСЯ НА СИМВОЛЫ "<",">"
// ПОЭТОМУ СГЕНЕРЕННАЯ ССЫЛКА ВЫВОДИТСЯ В ВИДЕ ТЕКСТА. ЗАМЕНИМ ЭТИ СИМВОЛЫ НА ТЕГИ И ОТДАДИМ УЖЕ В БРАУЗЕР
ТекстХТМЛ = СтрЗаменить(ТекстХТМЛ,"<","<");
ТекстХТМЛ = СтрЗаменить(ТекстХТМЛ,">",">");
ТекстХТМЛ = СтрЗаменить(ТекстХТМЛ," "," ");
//НЕЧЕГО ОСТАВЛЯТЬ МУСОР, УДАЛЯЕМ ВРЕМЕННЫЙ ФАЙЛ
УдалитьФайлы(ИмяВремФайла);
Возврат ТекстХТМЛ;
КонецФункции // СформироватьХТМЛИзТабдок()
Читаем его в виде текста и вроде бы все уже хорошо можно отдавать его как конечный результат в браузер и пожинать плоды.
... и здесь я наступил на грабли.
Ссылки в браузере отображались как простой текст. И я потратил достаточно много времени на поиск проблемы. В приложенном файле конфигурации, есть еще обработка, которую я написал для отладки, но к сожалению выяснилось, что размещенное на форме "Поле текстового документа" и "Поле HTML документа" на форме не отображает служебные символы, из-за чего и возникла такая проблема.
Тогда я стал смотреть файл, полученный после записи табличного документа в формате html и увидел, что все теги "<" ">", которые и служат в качестве управляющих символов в html заменились на "<" и ">" и еще вместо пробелов, которые разделяют команды расположенные между тегами, ставился " " поэтому браузер считал данную строку текстом. Поэтому в функцию СформироватьХТМЛИзТабдок и был добавлен следующий код:
ТекстХТМЛ = СтрЗаменить(ТекстХТМЛ,"<","<");
ТекстХТМЛ = СтрЗаменить(ТекстХТМЛ,">",">");
ТекстХТМЛ = СтрЗаменить(ТекстХТМЛ," "," ");
..и все стало отображаться как нужно.
Едем дальше.
Данные номенклатуры формируются функцией "СформироватьДанныеПоНоменклатуре(Код)"
Функция СформироватьДанныеПоНоменклатуре(Код) Экспорт
табдок = Новый ТабличныйДокумент;
Запрос = Новый запрос;
Запрос.Текст = "ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Код,
| Номенклатура.Наименование,
| Номенклатура.НаименованиеПолное,
| Номенклатура.Артикул,
| Номенклатура.Представление КАК НоменклатураПредставление,
| Номенклатура.ПутьККартинке
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Код = &Код";
Запрос.УстановитьПараметр("Код",Код);
Рез = Запрос.Выполнить();
Если Не Рез.Пустой() Тогда
Выборка = Рез.Выбрать();
Выборка.Следующий();
Макет = ПолучитьОбщийМакет("МакетЭлементаНоменклатуры");
Шапка = Макет.ПолучитьОбласть("Данные");
Шапка.Параметры.Заполнить(Выборка);
Табдок.Вывести(Шапка);
Если СокрЛП(Выборка.путьККартинке) <>"" Тогда // если поле не пустое, попытаемся вывести картинку
ОблКартинки = Макет.ПолучитьОбласть("Картинка");
СсылкаНаКартинку = ПолучитьСсылкуХТМЛПоАдресуКартинки(Выборка.ПутькКартинке);
ОблКартинки.Параметры.КартинкаНоменклатуры = СсылкаНаКартинку;
табдок.Вывести(ОблКартинки);
КонецЕсли;
КонецЕсли;
ТекстХТМЛ = СформироватьХТМЛИзТабдок(ТабДок);
//ДОБАВИМ В УЖЕ ГОТОВЫЙ HTML-КОД НАВИГАЦИОННУЮ ССЫЛКУ "НАЗАД" ХОТЯ ПО БОЛЬЛЬШОМУ СЧЕТУ МОЖНО ВОСПОЛЬЗОВАТЬСЯ В БРАУЗЕРЕ КНОПКОЙ "НАЗАД"
ХТМЛСсылкаНавигацииНазад = ПолучитьТекстНавигационнойСсылки_Назад();
ТекстХТМЛ = СтрЗаменить(ТекстХТМЛ,"</TABLE>","</TABLE> " + ХТМЛСсылкаНавигацииНазад);
Возврат ТекстХТМЛ;
КонецФункции // СформироватьДанныеПоНоменклатуре()
Обращу внимание на несколько моментов:
1. В данной случае используется Общий макет, с типом - "Табличный документ", а не схема компоновки данных как в первом случае.
2. Картинка выводится, если в элементе номенклатура, в реквизите "ПутькКартинке" указан путь до картинки.
3. Первоначально хотел выводить картинку с помощью формирования команды <img src="путь до картинки" alt="описание" /> , но как выяснилось по умолчанию браузеры не всегда отображают картинку находящуюся на сетевом ресурсе из-за настроек безопасности по умолчанию. Менять настройки браузеров на n-количестве рабочих мест - "это не наш метод"(с). Поэтому решил встраивать картинку прямо в код html путем кодирования картинки в строку BASE64 и помещая ее в соответствующую обертку типа
<img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="это что-то" />
Далее привожу все остальные вспомогательные функции использующиеся в функции СформироватьДанныеПоНоменклатуре()
// ВОЗВРАЩАЕТ КОД HTML ДЛЯ НАВИГАЦИОННОЙ ССЫЛКИ С ДЕЙТВИЕМ ПЕРЕХОДА НА ПРЕДЫДУЩУЮ СТРАНИЦУ
Функция ПолучитьТекстНавигационнойСсылки_Назад()
ТекстСсылки = "<a " + "href=""javascript:history.back(1)""" + ">Вернуться назад" + "</a>";
Возврат ТекстСсылки;
КонецФункции // ПолучитьТекстСсылкиНаСписокноменклатуры()
// ОСНОВНАЯ ВЫЗЫВАЕМАЯ ФУНКЦИЯ ФОРМИРОВАНИЯ КОДА ДЛЯ ВСТАВКИ КАРТИНКИ В БУДУЩИЙ HTML-КОД
Функция ПолучитьСсылкуХТМЛПоАдресуКартинки(ПутькКартинке)
СтрокаBase64 = ПолучитьКартинкуBase64(ПутькКартинке);
ТекстСсылки = "<img src="+"""" +СтрокаBase64 +""""+Символы.НПП+"alt="+""""+"картинка"+"""" + "/>";
Возврат ТекстСсылки;
КонецФункции // ()
// ВСПОМОГАТЕЛЬНАЯ ФУНКЦИЯ ВОЗВРАЩАЕТ КАРТИНКУ В ВИДЕ СТРОКИ BASE64
// ВЫЗЫВАЕТСЯ ИЗ "ПолучитьСсылкуХТМЛПоАдресуКартинки"
Функция ПолучитьКартинкуBase64(ПутькКартинке)
нКартинка = Новый Картинка(ПутькКартинке); // ПОЛУЧИМ КАРТИНКУ
фКартинки = Строка(нКартинка.Формат()); // ОПРЕДЕЛИМ ФОРМАТ КАРТИНКИ ДЛЯ ДАЛЬНЕЙШЕГО ФОРМИРОВАНИЯ HTML-ССЫЛКИ
ДвоичныеДанныеКартинки = нКартинка.ПолучитьДвоичныеДанные(); // ДЛЯ КОДИРОВАНИЯ В BASE64 ПОЛУЧИМ ДВОИЧНЫЕ ДАННЫЕ КАРТИНКИ
//КОДИРУЕМ КАРТИНКУ В BASE64
СтрокаBASE64 = Base64Строка(ДвоичныеДанныеКартинки);
//ФОРМИРУЕМ КОМАНДУ HTML ДЛЯ ВСТАВКИ КАРТИНКИ
СтрокаBASE64 = "data:image/"+ФКартинки+";base64,"+СтрокаBASE64;
// ДОПОЛНИТЕЛЬНО УБЕРЕМ ИЗ ПОЛУЧЕННОЙ СТРОКИ СЛУЖЕБНЫЕ СИМВОЛЫ, ТАК КАК НЕ ВСЕ БРАУЗЕРЫ КОРРЕКТНО ОТОБРАЖАЮТ КАРТИНКИ С ТАКИМИ СИМВОЛАМИ
СтрокаBASE64 = СтрЗаменить(СтрокаBASE64,символы.ВК,"");
СтрокаBASE64 = СтрЗаменить(СтрокаBASE64,символы.ПС,"");
Возврат СтрокаBASE64;
КонецФункции // ПолучитьКартинкуBase64()
// ВОЗВРАЩАЕТ ПЕРВИЧНУЮ ССЫЛКУ ДЛЯ БРАУЗЕРА С КОТОРОЙ НАЧИНАЕТСЯ РАБОТА С HTTP_СЕРВИСОМ
Функция СформироватьАдресСсылки() Экспорт
ИмяБазы = Макс (НСтр(СтрокаСоединенияИнформационнойБазы(),"Ref"));
КорневойURL = Метаданные.HTTPСервисы.ПросмотрНоменклатурыЧерезБраузер.КорневойURL;
Адрес = "http://localhost/"+ ИмяБазы + "/hs/"+КорневойURL+"/GetListNom";
Возврат Адрес;
КонецФункции // СформироватьАдресСсылки()
Еще немного про экспортную функцию СформироватьАдресСсылки() - она используется в обработке "ПроверкаФормированияHTML" для запуска браузера с первоначальной ссылкой на получение списка номенклатуры. Если сервер не локальный, то необходимо соответственно ее подправить.
Саму обработку для отладки особо описывать не буду, она просто вызывает функции общего модуля СформироватьСписокНоменклатурыНаСКД","СформироватьДанныеПоНоменклатуре" и выводит полученный html-код в одном поле и отображение его в html-поле. Но так как я выше уже говорил, решить возникшую проблему неправильного отображения в браузере мне не помогло, но на всякий случай удалять из конфигурации я ее не стал :)
К статье приложена выгрузка базы, которая реализует все вышенаписанное. Для отображения картинок нужно указать в элементах номенклатуры путь до картинки.
Вот в принципе и все. Буду рад комментариям, поучениям и всему остальному :) И надеюсь, что это кому-нибудь сэкономит немного времени.