Использование веб-сервисов в Internet Explorer

Источник: Gotdotnet

Введение

В статье рассматривается пример реализации интерфейса к веб-сервису через Internet Explorer (далее IE).

Что есть интересного в рассматриваемом решении:

  • Использование DHTML компонентов Webservice Behavior и Rowover Behavior
  • Отправка объектов из IE в веб-сервис.
  • Отображение данных веб-сервиса в IE.
  • Индикация процесса загрузки данных из веб-сервиса.
  • Использование островков данных XML в HTML страницах.
  • Частичное обновление данных на странице.

Работа с веб-серивсом напрямую через IE вещь экзотическая, но может таки понадобиться в ситуациях, когда требуется:

  1. Максимально снизить объем трафика.
  2. Часто обновлять некоторую информацию на HTML странице.
  3. Снизить нагрузку на веб сервер

Экономия трафика происходит за счет обмена только данными между IE и сервисом. Так как данные приходят в известном формате (XML), то их легко разобрать и вставить в нужные места HTML страницы. Процесс обмена данными с веб-сервисом значительно упрощается, за счет использования Webservice Behavior (http://msdn.microsoft.com/workshop/author/webservice/overview.asp) - DHTML компонента разработанного в Microsoft специально для этих целей.

Введение в DHTML компоненты.

DHTML компонент - это программа определяющая поведение (реакцию на разные события, например: "onclick", "onmouseover"…) объекта на HTML странице к которому этот компонент подсоединен. Компонент представляет собой файл с расширением HTC, программа в нем может храниться, например, на JavaScript. Так же сам DHTML компонент может вставлять объекты на страницу и определять поведение этих объектов.

Например, компонент "Coolbar" (http://msdn.microsoft.com/downloads/samples/internet/behaviors/library/coolbutton/coolbar_js.htm) из коллекции компонентов для IE (http://ie.components.microsoft.com/behaviors/), создает на странице панельку кнопок и реагирует на нажатия на эти кнопки. Другой компонент Rowover (http://msdn.microsoft.com/downloads/samples/internet/behaviors/library/rowover/rowover_js.htm) привязывается к существующей таблице и реагирует на события курсора на этой таблице.

Упрощенно: DHTML компонент - это сохраненный в файле скрипт, который привязывается к определенным событиям на HTML странице и/или создает HTML элементы, на события которых он тоже может реагировать.

Использовать DHTML компоненты стоит когда:

  1. HTML элементы с определенным поведением (скрипты клиентской стороны) повторяются у вас на многих страницах.
  2. К типичным HTML элементам нужно привязать нестандартное поведение. Пример: указанный ранее компонент "Rowover", который выделяет цветом строку таблицы, над которой находится курсор.
  3. На HTML странице требуется объект способный порождать собственные события, на которые могут подписаться другие объекты на странице. Пример такого компонента приведен в листинге 1.

Ниже приведен пример DHTML компонента "Updater". Этот компонент периодически обращается к веб-сервису, получает от него данные и передает их обработчикам на HTML странице, которые подписаны на событие "onupdate" этого компонента.


<EVENT NAME="onupdate" ID="update" /> //Событие обновления данных.
<METHOD NAME="GetUpdates" /> //Метод для получения данных от веб-сервиса.
<script language="javascript">
var webServicePath = "services/UpdateService.asmx?WSDL";
var webServiceMethod = "GetUpdates";
var blotterNodeName = "liabilities";
var updateData = new ActiveXObject("MSXML2.DOMDocument");
var intervalID;
var gbLoading;
var wsCallID;
updateData.async = false;
element.attachEvent("onreadystatechange", fnOnReadyStateChange);
function fnOnReadyStateChange()
{
gbLoading = (readyState != "complete")
if (!gbLoading)
{
init();
}
}
function GetUpdates()
{
wsCallID = webService.UpdateService.callService(onWSGetUpdates, webServiceMethod);
}
function init()
{
webService.useService(webServicePath,"UpdateService");
window.setInterval(uniqueID+".GetUpdates()", 5000);
// Раз в 5 секунд получаем обновления с веб сервиса.
}
function onWSGetUpdates(result) {
if (result.error) {
HandleError(result)
} else if(!result.error) {
updateData.loadXML(result.raw.xml);
var updateNode = updateData.selectSingleNode("//GetUpdatesResult");
var oEvent = document.createEventObject(); // Конструируем событие
oEvent.setAttribute("updateXML", updateNode);
// Добавляем полученные от веб-сервиса данные в аттрибуты события
update.fire(oEvent); //Порождаем событие.
}
}
</script>

Листинг 1.

Присоединение к событию различных обработчиков выглядит так:


window.Updater.attachEvent("onupdate", InsertUpdate)
// где InsertUpdate это функция обрабатывающая обновление.
function InsertUpdate()
{
var objUpdateXml = window.event.updateXML;
…….
}

Подключение HTML компонента к странице

Необходимый СSS:


.updater
{
behavior:url(htc/updater.htc);
}

Необходимый HTML.


<div class="updater" id=Updater></div>

Еще один пример HTML компонента есть в форуме www.gotdotnet.ru - это форма ввода сообщения.

Внутри себя компонент Webservice Behavior использует для взаимодействия с веб-сервисами ActiveX объект "Microsoft.XMLHTTP".

Webservice Behavior поддерживает состояние сессии.

Задача

Требуется разработать интерфейс для ввода и отображения заявок на покупку акций через Интернет. Информацию о заявках можно получить от веб-сервиса, заявки посылаются и модифицируются через тот же веб-сервис.

Функции интерфейса:

  1. Отправить заявку.
  2. Отобразить список заявок.
  3. Обновить список заявок.
  4. Отфильтровать и отсортировать список заявок.
  5. Отредактировать заявку.

Ограничения

У клиентов использующих интерфейс установлен браузер Internet Explorer не ниже версии 5.0.

Решение

Веб-сервис

На рисунке 1 приведена диаграмма классов описывающая структуру веб-сервиса используемого для взаимодействия с биржей. Синим цветом выделены классы, объекты которых будут передаваться между IE интерфейсом и веб-сервсом.

Рисунок 1

Обмен данными с веб-сервисом

Обмен данными с веб-сервисом происходит, по схеме указанной на рисунке 2. С помощью Web Service Behavior мы запрашиваем у веб-сервиса данные, полученный XML вставляем в XML островок (DataSource). При обновлении этого островка автоматически обновляется привязанный к нему потребитель данных (таблица или поле ввода). При отправке данных берем значения соответствующих контролов на странице и отправляем их в веб-сервис.

Рисунок 2

Инициализация веб-сервиса у клиента

Регистрируем Web Service Behavior на HTML странице. Для этого создаем CSS класс


.webservice { behavior: url(htc/webservice.htc);

который реализует поведение Web Service Behavior. После чего добавляем элемент DIV этого класса на страницу. Именно этот элемент и будет предоставлять возможности общаться в веб-сервисом.


<div id="webService" class="webservice" showProgress="true"></div>

Перед обращением к методам веб-сервиса необходимо проинициализировать его в Web Service Behavior вызвав метод:


webService.useService("services/OrdersEntryService.asmx?WSDL","Exch");
webService.useService(путь_к_сервису,имя_сервиса)

путь_к_сервису . Пример: "services/OrdersEntryService.asmx?WSDL"

имя_сервиса. Пример: "Exch" , здесь может быть любое слово. Сделано для возможности использовать несколько веб-сервисов на странице.

Пример использования веб-сервиса после его инициализации.


wsCallID = webService.Exch.callService(onWSSaveOrder, "AddOrder", order);

В момент инициализации Web Service Behavior получает от веб-сервиса его описание и на основе этого описания происходит последующее взаимодействие с сервисом.

Примечание : В описании к Web Service Behavior говорится, что в качестве веб-сервиса может выступать только сервис, доменная часть URL которого совпадает с доменной частью URL текущей страницы. Проще говоря, если адрес сервиса http://mydomen/OrdersEntryService.asmx?WSDL а адрес странички использующей сервис http://yourdomen/page.aspx, то они не будут между собой взаимодействовать. О том, как это обойти можно прочитать тут: http://msdn.microsoft.com/workshop/author/webservice/using.asp#Security_Issues

Отправить заявку

Для того, чтобы передать объект из IE интерфейса в веб-сервис необходимо:


сконструировать объект в IE с помощью JavaScript
var order = new Object();
order.Side = lbxSide.options[lbxSide.selectedIndex].value;;
order.Quantity = window.document.getElementById(?tbQty?).value;
order.Instrument = ddlAssets.options[ddlAssets.selectedIndex].value;;
order.TradingFloor = ddlTradingFloors.options[ddlTradingFloors.selectedIndex].value;;

передать объект с помощью HTML компонента Webservice Behavior.


wsCallID = webService.Exch.callService(onWSSaveOrder, webServiceMethod, order);
//callService(функция, название_веб_метода, параметры_веб_ метода)

функция - ссылка на функцию, которая будет вызвана компонентом Web Service Behavior в момент возвращения данных веб сервисом (callback функция). Пример - onWSSaveOrder.

название_веб_метода. Пример - "AddOrder".

параметры_веб_ метода. Параметры веб - метода через запятую.

Пример callback функции.


function onWSSaveOrder(result) {
if (result.error) {
locked = false;
HandleError(result)
} else if(!result.error) {
try
{
returnData.loadXML(result.raw.xml);
var rowsetNode = returnData.selectSingleNode("//OrderID");
alert("Заявка успешно сохранена\n № заявки: "+rowsetNode.text);
}finally
{
locked = false;
}
}
ChangeCursor("default");
}

Примечание: Исходная версия этого компонента от Microsoft не позволяет отправлять сервису в качестве параметров объекты. Толи это баг, толи фитча, но после изменения кода компонента в строках 675-677 мы сможем использовать объекты в качестве параметров сервиса. Исправленная версия компонента прилагается к статье.

Отобразить список заявок.

Список заявок отображается в таблице, которая привязывается к островку данных на странице.


<xml id="DataSource" ></xml>
<table datasrc=#DataSource>

При обновлении данных в XML островке обновляются данные и в таблице.

Список заявок запрашивается методом:


webService.Exch.callService(onWSGetBlotter, ?GetOrdersBlotter?, Filter,
sortColumn, sortMode);

Данные возвращаются в функцию onWSGetBlotter:


function onWSGetBlotter(result) {
if (result.error) {
locked = false;
HandleError(result)
} else if(!result.error) {
try
{ //Изменяем XML данные в источнике (XML островке) DataSource.
//При этом пришедшие данные отображаются в табличке.
returnData.loadXML(result.raw.xml);
var reportNode = returnData.selectSingleNode("//GetOrdersBlotterResult");
if(reportNode.hasChildNodes)
{
DataSource.documentElement = reportNode ;
}
}finally
{
locked = false;
ChangeCursor("default");
}
}
}

Обновить список заявок

Обновление списка заявок делается периодическими запросами данных от веб-сервиса, и обновлением на основе них XML островка. В развитие приведенной схемы обновлений желательно делать запрос не всех данных, а только обновлений. Это потребует реализации некоторой функциональности по сбору и накоплению обновлений для каждого пользователя на серверной стороне веб-сервиса. Более легкой оптимизацией процесса обновлений может стать введение для каждой строки таблицы скрытого поля, в котором будет содержаться хэш-код данных этой строки. При каждом обновлении сравнивать хэш-код строки пришедшей с сервера и хэш-код текущей строки и в случае различия обновлять строку.

Отфильтровать и отсортировать список заявок

Фильтрация выполняется с помощью передаваемого в метод "GetOrdersBlotter" массива структур типа FilterParameter. Опять же, здесь приходится очень полезной возможность передавать объекты и массивы объектов в веб сервис.


function SetFilter()
{
var ddlTradingFloors = window.document.getElementById(?ddlTradingFloors?);
var lbxSide = window.document.getElementById(?lbxSide?);
var ddlAssets = window.document.getElementById(?ddlAssets?);
var side = lbxSide.options[lbxSide.selectedIndex].value;
var instrument = ddlAssets.options[ddlAssets.selectedIndex].value;
var tradingFloor = ddlTradingFloors.options[ddlTradingFloors.selectedIndex].value;
locked = true;
Filter = new Array();
try
{
if (side != 0)
{
AddToFilter("Side",side);
}
if (instrument != 0)
{
AddToFilter("Instrument",instrument);
}
if (tradingFloor != 0)
{
AddToFilter("TradingFloor",tradingFloor);
}
}finally
{
locked = false;
}
ClearBlotter();
GetBlotter();
}
function AddToFilter(fieldName, value)
{
var temp = new Object();
temp.Name = fieldName
temp.Value = value;
Filter[Filter.length] = temp;
}

Для сортировки списка в веб сервис передаются имя колонки и направление сортировки.

Отредактировать заявку

При клике на заявке в списке, она открывается в форме для редактирования.

Здесь нам помогает использование еще одного DHTML компонента "Rowover Behavior".

Этот компонент позволяет привязывать свои обработчики к событию click на строке таблицы, раскрашивать в разные цвета строки таблицы, отображать курсор на строках таблицы.

Привязывается этот компонент к таблице по аналогии с компонентом Webservice Behavior.


<table rules="all" onrowclick="onOrder_click();" CLASS="rowover" selectable="true"
STRIPED="true" datasrc=#DataSource>

Элементы формы редактирования заявки связаны с XML островком данных.


<input type="text" id="tbID" size=5 datasrc=#DataSource datafld="OrderID">
<input type="text" id="tbQty" size=5 datasrc=#DataSource datafld="Quantity">
<select id="ddlStatuses" datasrc=#DataSource datafld="Status">

При изменении данных в островке обновляется и содержание формы.

Заключение

В статье описаны возможности использования DHTML компонентов для связи с веб-сервисами и функции привязки данных в Internet Explorer.

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

Работающий пример доступен по адресу: http://shelomanov.russia.webmatrixhosting.net/timetracker_cs_vs_1/ .

Исходные коды в формате проекта VS.NET 2003: http://www.gotdotnet.com/Community/UserSamples/Download.aspx?SampleGuid=A4E19629-742E-4A73-BC53-0081D4E470FE .

Все DHTML компоненты, использованные в примере, доработаны автором, об этом упомянуто в тексте статьи.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=8024