Технологія SOAP

Лабораторна робота

Технологія SOAP

Мета: отримання практичних навиків обміну даними між прикладенням C++ Builder і базою даних інформаційної системи в комп'ютерній мережі Internet з використанням технології SOAP.

Завдання:

Створити оригінальну (!) розподілену інформаційну систему на основі технології SOAP на прикладі системи з наступною архітектурою клієнт-серверної взаємодії:

    клієнтське прикладення де-небудь з Internet споживає Web-сервіс;

    Web-сервіс (через SOAP) виставляє об'єктні методи;

    об'єктні методи звертаються до віддалених даних де завгодно на Web.

Технологія SOAP

Малюнок 1

Клієнт SOAP використовує спеціальний UDDI-реєстр для локалізації Web-сервісу. В більшості випадків, замість керування WSDL безпосередньо, прикладення SOAP буде сконструйовано так, щоб використовувати специфічний тип порту і стиль скріплення, і динамічно конфігуруватиме адресу сервісу, що викликається з метою узгодження з сервісом, знайденим за допомогою UDDI.

Клієнтське прикладення створює повідомлення SOAP, яке є XML-документом, здатним здійснювати необхідні операції запиту / відповіді.

Клієнт посилає SOAP повідомлення JSP або ASP-сторінці на Web-сервері, що слухає запити SOAP.

Сервер SOAP аналізує пакет SOAP і викликає відповідний метод об'єкту в його області, передаваній в параметрах SOAP-документа. Перед ухваленням повідомлення SOAP-сервером вузли проміжної обробки можуть виконувати спеціальні функції, як вказано в заголовках SOAP.

Запрошуваний об'єкт виконує позначену функцію і повертає дані SOAP-серверу, який запаковує відповідь в конверт SOAP. Потім сервер «кладе» конверт SOAP в об'єкт відповіді (наприклад, сервлет або COM-об'єкт), який і посилається назад запитуючій машині.

Клієнт одержує об'єкт, «знімає» конверт SOAP і посилає у відповідь документ програмі, що спочатку запитала його, завершуючи цикл запиту / відповіді.

Delphi дозволяє створювати як сервери, так і клієнти Web Services. Ми почнемо розгляд із створення сервера.

Створення сервера Web Services

Створення сервера Web Services в Delphi складається з наступних етапів:

1. Опис інтерфейсу сервера, тобто методів, які будуть доступні для виклику клієнту;

2. Реалізація методів сервера;

3. Створення проекту Delphi і включення в нього результатів перших двох кроків.

У Delphi при створенні сервера Web Services методи, доступні для виклику клієнту, описуються у вигляді invokable інтерфейсів. Invokable інтерфейс - це інтерфейс, для методів якого доступна RTTI (інформація про типи на етапі виконання). Для того, щоб із звичайного інтерфейсу зробити invokable досить вказати директиву компіляції {$M+}. Після цього всі нащадки і сам інтерфейс міститимуть RTTI. У ієрархії VCL вже є такий інтерфейс IInvokable. Таким чином, при написанні сервера простіше всього успадкувати інтерфейс IInvokable. Крім того, необхідно зареєструвати свій інтерфейс в invocation registry. Реєстрація дозволяє серверу визначити клас, що реалізовує методи інтерфейсу, а клієнту одержати опис методів, підтримуваних сервером. Реєстрація здійснюється викликом методу InvRegistry.RegisterInterface у секції initialization модуля.

Оскільки інтерфейс використовується не тільки сервером, але і клієнтом, то бажано визначити його в окремому модулі Delphi.

Для прикладу ми розробимо сервер, який здійснюватиме перерахунок грошей з € у гривні і назад. У RAD Delphi оберімо пункт меню File | New | Unit. У одержаному порожньому модулі визначимо інтерфейс сервера:

unit u_Intrf;

interface

type

IEncodeDecode = interface(IInvokable)

['{9298D805-A2FB-4860-994E-11CC5BD36025}']

// Конвертація Евро в Гривни

function EuroToUk(Value: Currency): Currency; stdcall;

// Конвертація Гривнею в Евро

function UkToEuro(Value: Currency): Currency;

stdcall;

end;

implementation

uses InvokeRegistry;

initialization

InvRegistry.RegisterInterface(TypeInfo(IEncodeDecode));

end.

Зверніть увагу, що рядок ['{9298D805-A2FB-4860-994E-11CC5BD36025}'] – це GUID інтерфейсу. Для коректної роботи прикладу необхідно згенерувати його, а не вводити уручну або копіювати з наведеного тексту. Генерація GUID в RAD Delphi викликається натисненням Ctrl+Shift+G.

У разі використання у функціях інтерфейсу скалярних типів даних генерація SOAP-повідомлень відбувається автоматично без додаткових зусиль з боку програміста. Якщо потрібно використовувати складні типи даних, такі як статичні масиви, набори і класи, то необхідно створити і зареєструвати клас-спадкоємець від TRemotableXS і перевизначити методи XSToNative і NativeToXS. Дані методи конвертують строкове і бінарне представлення даних одне в одне.

Найбільш простим способом реалізації інтерфейсу на сервері є створення і реєстрація в invocation реєстрі класу-спадкоємця від TInvokableClass. Клас TInvokableClass має дві чудові особливості:

    Invocation реєстр знає про те, як створити екземпляр цього класу і його спадкоємців при запиті клієнтом викликів методів інтерфейсу;

    Оскільки клас TInvokableClass є спадкоємцем від TInterfacedObject, то він уміє звільнити пам'ять у разі, коли кількість посилань на нього дорівнює 0, що полегшує програмісту життя.

Текст модуля реалізації представлено далі:

unit u_Impl;

interface

uses InvokeRegistry, u_Intrf;

type

TEncodeDecode = class(TInvokableClass, IEncodeDecode)

protected

function EuroToUk(Value: Currency): Currency; stdcall;

function UkToEuro(Value: Currency): Currency; stdcall;

end;

implementation

{ TEncodeDecode }

function TEncodeDecode.UkToEuro(Value: Currency): Currency;

begin

Result := Value / 6.2;

end;

function TEncodeDecode.EuroToUk(Value: Currency): Currency;

begin

Result := Value * 6.2;

end;

initialization

InvRegistry.RegisterInvokableClass(TEncodeDecode);

end.

У випадку, якщо Ви не хочете успадковувати клас від TInvokableClass, необхідно створити і зареєструвати метод-фабрику класу, який зможе створювати екземпляри класу. Метод повинен бути типу TCreateInstanceProc = procedure(out obj: TObject). При цьому екземпляр повинен уміти ліквідовувати себе, якщо кількість посилань використовуючих його клієнтів стане нульовою. При реєстрації такого класу методу InvRegistry.RegisterInvokableClass другим параметром необхідно передати ім'я методу-фабрики класу.

Залишився останній крок - створення проекту прикладення. У RAD оберімо команду меню File | New | Other і із закладки WebServices оберімо значок SOAP Server Application. Буде виведений діалог вибору формату прикладення Web Services (мал. 2).

Малюнок 2

Оберімо формат CGI Stand-alone executable. При цьому буде створений проект з Web-модулем, що містить три компоненти: HTTPSoapDispatcher, HTTPSoapPascalInvoker, WSDLHTMLPublish (мал. 3).

Малюнок 3

    HTTPSoapDispatcher одержує і обробляє SOAP-повідомлення, перенаправляючи їх invoke інтерфейсам, зареєстрованим у прикладенні. Таким чином HTTPSoapDispatcher є диспетчером, відповідальним за прийом, розподіл і відправку SOAP-повідомлень. Інтерпретація запитів і виклик методів інтерфейсів здійснюється іншим компонентом, вказаним у властивості Dispatcher (HTTPSoapPascalInvoker1).

    HTTPSoapDispatcher автоматично реєструє себе в Web-модулі, як автодиспетчуємий. При цьому всі запити передаються HTTPSoapDispatcher, що позбавляє Вас від необхідності створювати оброблювачі запитів Web-модуля.

    WSDLHTMLPublisH2 генерує і видає за запитом клієнта опис інтерфейсу сервера.

На запит про необхідність створення інтерфейсу необхідно клацнути по кнопці Yes (мал . 4.4).

Малюнок 4

У вікні діалогу, яке з’явилося, треба задати ім’я служби (мал. 5).

Малюнок 5

Далі в проект необхідно підключити файли з описом і реалізацією інтерфейсу. Для цього в RAD оберімо команду меню Project | Add to project і у діалозі, що з'явився, оберімо модулі з описом і реалізацією методів інтерфейсу (мал. 6).

Малюнок 6

Тепер можна зберегти проект, побудувати його і розташувати одержаний виконуваний файл у каталозі c:\Inetpub\Scripts Web-сервера IIS. Сервер готовий до роботи.

Створення клієнта Web Services

Умовно розробку клієнта можна розділити на дві частини:

    Отримання опису інтерфейсу сервера;

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

У разі розробки сервера на Delphi існує модуль з описом інтерфейсу сервера на мові Object Pascal, т.ч перший етап може бути пропущений. У випадку якщо сервер був розроблений із використанням інших мов або модуль з описом інтерфейсу недоступний, необхідно одержати опис інтерфейсу у форматі WSDL або XML. Перший варіант – це попросити файл з описом у розроблювача, другий - згенерувати опис самому. Для цього досить запустити Web-браузер і в рядку адреси набрати команду: http://<ім’я сервера>/<папка Scripts>/< ім’я прикладення-сервера>/wsdl. У даному прикладі сервер розташовано на локальному комп’ютері, як Web-сервер IIS, тому рядок адреси виглядає так: http://localhost/Scripts/SOAPServerProject.exe/wsdl. При цьому на екран буде виведена таблиця з описом інтерфейсів сервера (мал. 7).

Малюнок 7

Необхідно обрати в таблиці інтерфейс IEncodeDecode, що цікавить нас, при цьому буде згенеровано опис інтерфейсу у форматі XML:

<?xml version="1.0" encoding="utf-8" ?>

- <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema" name="IEncodeDecodeservice" targetNamespace="http://tempuri.org/" xmlns:tns="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/">

- <message name="EuroToUk0Request">

<part name="Value" type="xs:double" />

</message>

- <message name="EuroToUk0Response">

<part name="return" type="xs:double" />

</message>

- <message name="UkToEuro1Request">

<part name="Value" type="xs:double" />

</message>

- <message name="UkToEuro1Response">

<part name="return" type="xs:double" />

</message>

- <portType name="IEncodeDecode">

- <operation name="EuroToUk">

<input message="tns:EuroToUk0Request" />

<output message="tns:EuroToUk0Response" />

</operation>

- <operation name="UkToEuro">

<input message="tns:UkToEuro1Request" />

<output message="tns:UkToEuro1Response" />

</operation>

</portType>

- <binding name="IEncodeDecodebinding" type="tns:IEncodeDecode">

<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />

- <operation name="EuroToUk">

<soap:operation soapAction="urn:u_Intrf-IEncodeDecode#EuroToUk" style="rpc" />

- <input message="tns:EuroToUk0Request">

<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:u_Intrf-IEncodeDecode" />

</input>

- <output message="tns:EuroToUk0Response">

<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:u_Intrf-IEncodeDecode" />

</output>

</operation>

- <operation name="UkToEuro">

<soap:operation soapAction="urn:u_Intrf-IEncodeDecode#UkToEuro" style="rpc" />

- <input message="tns:UkToEuro1Request">

<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:u_Intrf-IEncodeDecode" />

</input>

- <output message="tns:UkToEuro1Response">

<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:u_Intrf-IEncodeDecode" />

</output>

</operation>

</binding>

- <service name="IEncodeDecodeservice">

- <port name="IEncodeDecodePort" binding="tns:IEncodeDecodebinding">

<soap:address location="http://localhost/Scripts/SOAPServerProject.exe/soap/IEncodeDecode" />

</port>

</service>

</definitions>

Збережіть його у файлі IEncodeDecode.xml. Отже, тим або іншим способом файл з описом у форматі XML опинився у нас в руках, тепер необхідно експортувати його в Delphi. При експорті Delphi згенерує модуль з описом інтерфейсу на мові Object Pascal. Оберімо команду меню File | New | Other, перейдемо на закладку WebServices і оберімо ікону WSDL Importer. При цьому на екрані з'явиться діалог імпорту опису (мал. 4.8).

Використовуючи кнопку … діалогу, вкажемо одержаний раніше файл SOAPClient.xml, і натиснемо кнопку Finish. Модуль Delphi з описом інтерфейсу готовий.

Малюнок 8

// ************************************************************************ //

// The types declared in this file were generated from data read from the

// WSDL File described below:

// WSDL : D:\SOAP\SOAPClient\IEncodeDecode.xml

// Encoding : utf-8

// Version : 1.0

// (05.02.2006 10:06:04 - 1.33.2.5)

// ************************************************************************ //

unit IEncodeDecode1;

interface

uses InvokeRegistry, SOAPHTTPClient, Types, XSBuiltIns;

type

// ************************************************************************ //

// The following types, referred to in the WSDL document are not being represented

// in this file. They are either aliases[@] of other types represented or were referred

// to but never[!] declared in the document. The types from the latter category

// typically map to predefined/known XML or Borland types; however, they could also

// indicate incorrect WSDL documents that failed to declare or import a schema type.

// ************************************************************************ //

// !:double - "http://www.w3.org/2001/XMLSchema"

// ************************************************************************ //

// Namespace : urn:u_Intrf-IEncodeDecode

// soapAction: urn:u_Intrf-IEncodeDecode#%operationName%

// transport : http://schemas.xmlsoap.org/soap/http

// style : rpc

// binding : IEncodeDecodebinding

// service : IEncodeDecodeservice

// port : IEncodeDecodePort

// URL : http://localhost/Scripts/SOAPServerProject.exe/soap/IEncodeDecode

// ************************************************************************ //

IEncodeDecode = interface(IInvokable)

['{2F701C83-3E4D-2403-7EA6-5BC2C987131C}']

function EuroToUk(const Value: Double): Double; stdcall;

function UkToEuro(const Value: Double): Double; stdcall;

end;

function GetIEncodeDecode(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): IEncodeDecode;

implementation

function GetIEncodeDecode(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): IEncodeDecode;

const

defWSDL = 'D:\SOAP\SOAPClient\IEncodeDecode.xml';

defURL = 'http://localhost/Scripts/SOAPServerProject.exe/soap/IEncodeDecode';

defSvc = 'IEncodeDecodeservice';

defPrt = 'IEncodeDecodePort';

var

RIO: THTTPRIO;

begin

Result := nil;

if (Addr = '') then

begin

if UseWSDL then

Addr := defWSDL

else

Addr := defURL;

end;

if HTTPRIO = nil then

RIO := THTTPRIO.Create(nil)

else

RIO := HTTPRIO;

try

Result := (RIO as IEncodeDecode);

if UseWSDL then

begin

RIO.WSDLLocation := Addr;

RIO.Service := defSvc;

RIO.Port := defPrt;

end else

RIO.URL := Addr;

finally

if (Result = nil) and (HTTPRIO = nil) then

RIO.Free;

end;

end;

initialization

InvRegistry.RegisterInterface(TypeInfo(IEncodeDecode), 'urn:u_Intrf-IEncodeDecode', 'utf-8');

InvRegistry.RegisterDefaultSOAPAction(TypeInfo(IEncodeDecode), 'urn:u_Intrf-IEncodeDecode#%operationName%');

end.

Переходимо до другого етапу - безпосередньому створенню клієнта. Створимо заготівку нового додатку командою File | New | Application. На головній формі розташуймо рядок введення, дві кнопки і компонент HTTPRIO із закладки WebServices (мал. 9).

Малюнок 4.9

Компонент HTTPRIO призначений для виклику серверів через SOAP. Вкажемо у властивості URL значення http://localhost/Scripts/SOAPServerProject.exe/soap/IEncodeDecode, - шлях до сервера. Далі включимо в проект модуль Delphi з описом інтерфейсу сервера і вкажемо його в секції uses головної форми проекту.

Тепер можна переходити до написання коду виклику методів сервера. Оброблювачі подій натиснення на кнопки UkToEuro і EuroToUk виглядатимуть так:

procedure TForm1.UkToEuroClick(Sender: TObject);

var

X:IEncodeDecode;

R:Currency;

begin

X := HTTPRIO1 as IEncodeDecode;

R := X.UkToEuro(StrToCurr(Summa.Text));

ShowMessage(CurrToStr(R)+' €');

end;

procedure TForm1.EuroToUkClick(Sender: TObject);

var

X:IEncodeDecode;

R:Currency;

begin

X := HTTPRIO1 as IEncodeDecode;

R := X.EuroToUk(StrToCurr(Summa.Text));

ShowMessage(CurrToStr(R)+'Грн');

end;

Залишилося запустити проект на виконання і переконатися в його працездатності (мал. 10).

Малюнок 10