Создание автономной Web-службы в Delphi 7 с помощью комплекта Indy. Часть 2

Серж Досюков (Serge Dosyukov) Майк Фэм (Mike Pham)

Часть 1

Введение

Что делать, если есть необходимость в web-службе с двумя и более модулями TSoapDataModule? В этой статье рассказывается о том, как создать автономную Web-службу, используя комплект Indy и Delphi 7, и описывается один из способов создания подобной службы. Прежде чем читать данную статью, рекомендуется ознакомиться с демонстрационной версией модуля SOAPDataModule, поставляемого вместе с Delphi 7. В демонстрационной версии не только показывается, как создать SOAP-модуль данных с вызываемыми методами в том же самом интерфейсе, но также демонстрируется, как можно использовать свойство SOAPServerIID соединения TSoapConnection для извлечения нужного интерфейса прямо из данной службы.

Значительная часть этой статьи основана на сообщениях Бруно (Джин-Мери Бабет) из сетевой телеконференции (borland.public.delphi.webservices.soap). Эти сообщения позволили пролить свет на то, как SOAP-соединение взаимодействует с web-службами. Вот ссылка на сообщение, которое дало начало этому небольшому проекту:

Из этого сообщения можно заключить, что соединение TSoapConnection всегда получает интерфейс IAppServer из модуля TSoapDataModule вне зависимости от названия интерфейса, заданного для данного указателя URL. Сначала отсылка осуществляется с помощью операций SOAPAction, но в случае неудачи, в конце концов, будет использоваться указатель URL. Интерфейс IAppServer всегда посылается обратно приложению-клиенту, а конструктором этого интерфейса является первый зарегистрированный модуль данных (скорее всего, это происходит из-за того, что перемещение SDM-модуля в Delphi-проекте раньше других модулей приводит к смене поставщика услуг).

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

Три вместо одного!

Во-первых, необходимо стандартным образом создать новую Web-службу с поддержкой системы DataSnap (File->New->Other, WebServices, а затем выбрать SOAP Server Application). SOAP-приложение будет создано как файл, исполняемый сервером Web App Debugger, с именем класса (Class Name) TheThreeSDMs. В данный момент не нужно создавать никакого дополнительного интерфейса.

Теперь, когда есть каркас SOAP-сервера, нужно добавить три модуля данных SOAP Data Module. В данной статье они названы SDM1, SDM2 и SDM3. Для каждого модуля данных нужно задать параметр, определяющий поставщика услуг. Далее будут использованы три набора данных ClientDataSets, а также будут загружены некоторые данные из базы примеров MyBase и добавлены некоторые компоненты TDataSetProviders. Теперь имеется действующий SOAP-сервер и нужно запустить данный проект, по крайней мере, один раз, чтобы зарегистрировать его на сервере WebApp Debugger. После этого следует обратить внимание на язык WSDL, генерируемый для данной службы.

Как можно видеть, в самом деле, имеется три зарегистрированных модуля SOAP-данных. А сейчас нужно создать приложение-клиент для тестирования эти модулей данных. Запустите новое приложение, используя обычные и "slap on" компоненты: три компоненты соединения TSoapConnection, три набора данных ClientDataSets, три источника данных Data Sources и три инструмента DBGrid. Укажите все SOAP-соединения для каждого SDM-модуля по следующему образцу: http://[servername]/soap/[interfacename]. После этого следует попытаться задать имя поставщика услуг для каждого хранилища данных. Возвращается именно dspSDM1, вне зависимости от того, какой модуль SOAP-данных используется. Опять таки, как пишет Бруно в своих сообщениях, это происходит из-за того, что в качестве конечной точки вызывается интерфейс IAppServer, а не указатель URL.

Решение!

Время прогона, период проектирования… Работа никогда не идет так, как хотелось бы. А хотелось бы просто использовать указатель URL, а не конструктор интерфейса IappServer. Как же добиться этого? Чтобы ответить на этот вопрос важно понимать суть самой проблемы. Следует обратить внимание на то, что в Delphi 7 существует свойство SOAPServerIID для соединения TSoapConnection. Для него всегда установлено значение "IAppServerSOAP - {C99F4735-D6D2-495C-8CA2-E53E5A439E61}". Это значение можно изменить, но тогда для свойства Connected нельзя установить значение True (это можно сделать, если задать значение False для параметра UseSOAPAdapter, но это приводит к вызову интерфейса IAppServer вместо IAppServerSOAP, нового интерфейса, введенного в Delphi 6). Заглянув в модуль SOAPConn, можно заметить, что, скорее всего, не происходит создания основного RIO-компонента. В то же время, если посмотреть на демонстрационную версию модуля SOAPDataModule можно увидеть, что существует возможность изменения свойства SOAPServerIID. Однако в настоящем случае необходимо иметь возможность импортирования языка WSDL из сервера и, кроме того, это приводит к утрате ряда возможностей в период проектирования.

Предлагаемое решение основано на понимании того, как соединение TSoapConnection получает информацию в зависимости от интерфейса модуля SOAP-данных. На серверной стороне интерфейс обычно регистрируется с помощью модуля InvokeRegistry. Во время импорта языка WSDL, как и в демонстрационной версии, он регистрирует себя в приложении-клиенте тоже с помощью модуля InvokeRegistry. Это необходимо для того, чтобы во время прогона соединение TSoapConnection могло найти правильный интерфейс для обслуживания обращений к данной службе (в случае демонстрационной версии это интерфейс IDataMod). Однако это приводит к утрате некоторых возможностей в период проектирования. Далее будет рассмотрено, как преодолеть данную проблему, которая, похоже, является единственным препятствием на пути достижения цели этой статьи. Если хорошо подумать, эта проблема не так уж и сложна. Интегрированная среда разработки ничего не знает о том, как вызывать разделы инициализации модулей текущего проекта, так что в период проектирования в модуле InvokeRegistry интерфейсы отсутствуют. Решением проблемы является создание для периода проектирования специального пакета с модулем, содержащим интерфейсы-потомки (такие как ISDM2).

Снова откройте проект сервера и добавьте новый модуль под названием uTheThreeSDMsIntf. Сначала нужно объединить все SDM-интерфейсы сервера в этот модуль и задать оператор uses clause каждого SDM-модуля для использования нового модуля интерфейсов. Кроме того, обращение к службе должно быть передано функции InvRegistry.RegisterInterface() для вызова раздела инициализации нового модуля. Ниже приведен соответствующий программный код для модуля uTheThreeSDMsIntf.

unit uTheThreeSDMsIntf;

interface

uses
InvokeRegistry, SOAPMidas;

type

ISDM1 = interface(IAppServerSOAP)
['{E88F935E-94E4-4AFE-8A39-7453DB229286}']
end;

ISDM2 = interface(IAppServerSOAP)
['{063C7F1A-66B1-44C8-95A6-0FA025BF7028}']
end;

ISDM3 = interface(IAppServerSOAP)
['{6567D343-440B-42F8-931A-28735A8A3221}']
end;

implementation

initialization
InvRegistry.RegisterInterface(TypeInfo(ISDM1));
InvRegistry.RegisterInterface(TypeInfo(ISDM2));
InvRegistry.RegisterInterface(TypeInfo(ISDM3));
finalization
InvRegistry.UnRegisterInterface(TypeInfo(ISDM3));
InvRegistry.UnRegisterInterface(TypeInfo(ISDM2));
InvRegistry.UnRegisterInterface(TypeInfo(ISDM1));
end.

Теперь, когда все интерфейсы размещены централизованно, нужно создать такой пакет для работы в период проектирования, чтобы в интегрированной среде разработки можно было вызвать инициализацию данного модуля во время его загрузки. Создайте новый пакет и установите для него свойство Design-Time Only. После этого остается только добавить модуль SDM-интерфейсов, а затем скомпилировать и установить полученный пакет.

А сейчас перейдем к приложению-клиенту. Необходимо установить указатель URL на заданную по умолчанию конечную точку протокола SOAP так, чтобы он указывал на адрес текущего WAD-проекта, т.е. "http://localhost:8081/threesdms.TheThreeSDMs/soap". Кроме того, нужно скопировать GUID-строку интерфейса и вставить ее в каждое свойство SOAPServerIID соединения TSoapConnection, точно таким же образом, как и в демонстрационной версии модуля SOAPDataModule. Отличие состоит в том, что теперь имеются соединения, действующие в период проектирования, а также есть возможность заполнения таблицы данных в обеих ситуациях. Последнее, что остается сделать в приложении-клиенте - это добавить модуль uTheThreeSDMsIntf к оператору uses. В загрузку был включен модуль под названием uSoapIntf, который регистрирует редактор свойств, после чего свойство SOAPServerIID может отображать все зарегистрированные интерфейсы клиентской части. Кроме того, теперь можно подсоединятся к SOAP-серверу, как это показано на приведенном ниже рисунке. Это не только значительно сокращает число операций копирования и вставки, но также позволяет отображать все интерфейсы. Следует отметить, что имя описанного выше модуля существует только в текущем проекте.

Готово! Теперь можно задать параметр ProviderName для каждого поставщика услуг и все будет работать должным образом, как в период проектирования, так и во время прогона. Вот, по сути дела, и все!
Пример проекта можно посмотреть здесь.


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