Теперь посмотрим, как мы можем работать с составными сообщениями на стороне клиента. Нам необходимо получить ответ HTTP-сервиса, распаковать вложенные в ответ сообщения и показать их содержимое.
В форму нашей демонстрационной обработки добавим реквизиты типа Строка:
Перетащим их в окно элемента формы и свяжем их с соответствующими элементами управления:
Также добавим в форму команду ОтправитьHTTPЗапрос. Из обработчика этой команды мы вызываем серверную процедуру Выполнить_HTTPЗапрос(), в которой и происходят получение, разбор и отображение ответа HTTP-сервиса (листинг 6.111).
Листинг 6.111. Процедура «Выполнить_HTTPЗапрос()»
&НаСервере
Процедура Выполнить_HTTPЗапрос()
HTTPСоединение = Новый HTTPСоединение("localhost");
// Сформировать запрос, отправить на сервер и получить ответ.
Запрос = Новый HTTPЗапрос("Web_1C/hs/multipart");
Ответ = HTTPСоединение.Получить(Запрос);
// Разобрать ответ на составные части.
// Результат представляет собой структуру, содержающую текст и картинки из составного HTTP-сообщения.
Результат = ПрочитатьСообщение(Ответ.Заголовки, Ответ.ПолучитьТелоКакДвоичныеДанные());
HTTPСообщение = Результат.Сообщение;
// Создать объекты Картинка из двоичных данных изображений, полученных с сервера.
Картинка1 = Новый Картинка(Результат.Картинка1);
Картинка2 = Новый Картинка(Результат.Картинка2);
// Поместить полученные картинки во временное хранилище.
АдресКартинки1 = ПоместитьВоВременноеХранилище(Картинка1);
АдресКартинки2 = ПоместитьВоВременноеХранилище(Картинка2);
КонецПроцедуры
Отправка запросов и получение ответов от HTTP-сервисов подробно рассматривались в первой главе в разделе «». Поэтому в данном примере мы не будем еще раз на этом останавливаться. Поясним только моменты, касающиеся работы с двоичными данными.
После отправки запроса к нашему HTTP-сервису, определенному ранее, мы вызываем функцию ПрочитатьСообщение() и передаем туда заголовки и тело ответа, полученное методом ПолучитьТелоКакДвоичныеДанные(). В этой функции и выполняется разбор полученного от сервиса составного сообщения (листинг 6.112).
Листинг 6.112. Функция «ПрочитатьСообщение()»
&НаСервереБезКонтекста
Функция ПрочитатьСообщение(Заголовки, Тело)
Разделитель = ПолучитьРазделительСоставногоСообщения(Заголовки);
Маркеры = Новый Массив();
Маркеры.Добавить("==" + Разделитель);
Маркеры.Добавить("==" + Разделитель + Символы.ПС);
маркеры.Добавить("==" + Разделитель + Символы.ВК);
Маркеры.Добавить("==" + Разделитель + Символы.ВК + Символы.ПС);
Маркеры.Добавить("==" + Разделитель + "==");
Текст = Неопределено;
Изображение1 = Неопределено;
Изображение2 = Неопределено;
ЧтениеДанных = Новый ЧтениеДанных(Тело);
// Перейти к началу первой части.
ЧтениеДанных.ПропуститьДо(маркеры);
// Прочитать в цикле все части тела сообщения.
Пока Истина Цикл
Часть = чтениеДанных.ПрочитатьДо(Маркеры);
Если Не Часть.МаркерНайден Тогда
// Неправильно сформированное сообщение
Прервать;
КонецЕсли;
ЧтениеЧасти = Новый ЧтениеДанных(Часть.ОткрытьПотокДляЧтения());
ЗаголовкиЧасти = ПрочитатьЗаголовки(ЧтениеЧасти);
ИмяЧасти = ПолучитьИмяСообщения(ЗаголовкиЧасти);
Если ИмяЧасти = "MessageText" Тогда
Текст = ЧтениеЧасти.ПрочитатьСимволы();
ИначеЕсли имяЧасти = "image1" Тогда
Изображение1 = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();
ИначеЕсли имяЧасти = "image2" Тогда
Изображение2 = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();
КонецЕсли;
Если Часть.ИндексМаркера = 4 Тогда
// Прочитана последняя часть
Прервать;
КонецЕсли;
КонецЦикла;
Возврат Новый Структура("Сообщение,Картинка1,Картинка2", Текст, Изображение1, Изображение2);
КонецФункции
В этой функции мы сначала получаем разделитель составного сообщения из заголовков сообщения с помощью функции ПолучитьРазделительСоставногоСообщения(), приведенной в листинге 6.113.
Затем формируем массив маркеров, описывающих все возможные маркеры, разделяющие составное сообщение на части.
После этого создаем объект ЧтениеДанных на основе тела ответа, переданного в функцию в виде двоичных данных, и методом ПропуститьДо() позиционируемся на чтении первой части сообщения. В цикле методом ПрочитатьДо() объекта ЧтениеДанных мы читаем сообщение по частям вплоть до последнего маркера.
Для каждой прочитанной части мы получаем заголовки этой части сообщения в виде объекта Соответствие с помощью функции ПрочитатьЗаголовки(), приведенной в листинге 6.114. А также получаем имя вложенного сообщения из заголовков этой части сообщения с помощью функции ПолучитьИмяСообщения(), приведенной в листинге 6.115.
Затем в зависимости от имени прочитанной части получаем либо текст, либо двоичные данные картинок. И после окончания чтения всех частей сообщения упаковываем эти значения в структуру, которую функция возвращает в процедуру Выполнить_HTTPЗапрос() – см. листинг 6.111. После чего в этой процедуре текст и картинки распаковываются и отображаются в соответствующих элементах формы обработки.
Вспомогательную функцию ПолучитьРазделительСоставногоСообщения() заполним следующим образом (листинг 6.113).
Листинг 6.113. Функция «СоздатьСообщение_Изображение()»
&НаСервереБезКонтекста
Функция ПолучитьРазделительСоставногоСообщения(Заголовки)
ТипСодержимого = Заголовки.Получить("Content-Type");
Свойства = СтрРазделить(ТипСодержимого, ";", Ложь);
Граница = Неопределено;
Для Каждого Свойство Из Свойства Цикл
Части = СтрРазделить(Свойство, "=", Ложь);
ИмяСвойства = СокрЛП(Части[0]);
Если ИмяСвойства <> "boundary" Тогда
Продолжить;
КонецЕсли;
Граница = СокрЛП(Части[1]);
Прервать;
КонецЦикла;
Возврат Граница;
КонецФункции
При поиске строки-разделителя составного сообщения из заголовков предполагается, что значение разделителя задается в заголовке Content-Type в виде Content-Type: multipart/form-data; boundary<=Разделитель>.
Для разбора строк во всех вспомогательных функциях используется функция глобального контекста СтрРазделить().
Вспомогательную функцию ПрочитатьЗаголовки() заполним следующим образом (листинг 6.114).
Листинг 6.114. Функция «ПрочитатьЗаголовки()»
&НаСервереБезКонтекста
Функция ПрочитатьЗаголовки(Чтение)
Заголовки = Новый Соответствие();
Пока Истина Цикл
Строка = Чтение.ПрочитатьСтроку();
Если Строка = "" Тогда
Прервать;
КонецЕсли;
Части = СтрРазделить(Строка, ":");
ИмяЗаголовка = СокрЛП(Части[0]);
Значение = СокрЛП(Части[1]);
Заголовки.Вставить(ИмяЗаголовка, Значение);
КонецЦикла;
Возврат Заголовки;
КонецФункции
Вспомогательную функцию ПолучитьИмяСообщения() заполним следующим образом (листинг 6.115).
Листинг 6.115. Функция «ПолучитьИмяСообщения()»
&НаСервереБезКонтекста
Функция ПолучитьИмяСообщения(Заголовки)
Описание = Заголовки.Получить("Content-Disposition");
Свойства = СтрРазделить(Описание, ";", Ложь);
Имя = Неопределено;
Для Каждого Свойство Из Свойства Цикл
Части = СтрРазделить(Свойство, "=", Ложь);
ИмяСвойства = СокрЛП(Части[0]);
Если ИмяСвойства <> "name" Тогда
Продолжить;
КонецЕсли;
Имя = СокрЛП(Части[1]);
Прервать;
КонецЦикла;
Возврат Имя;
КонецФункции
В этой функции имя сообщения получается из заголовка Content-Disposition в виде Content-Disposition: form-data; name<=Имя сообщения>.