Sign in Join
Wiki copytemporary

Web Service Toolkit/ru

English · Français · Português · Русский

«Web Service Toolkit» - это пакет веб-сервисов для FPC, Lazarus и Delphi, «Web Service Toolkit» предназначен для облегчения использования и обслуживания веб-сервисов пользователями FPC, Lazarus и Delphi.

Сторона клиента (использование сервиса)

Mailing list: http://groups.google.com/group/wst-list

Subversion checkout: svn co https://svn.code.sf.net/p/lazarus-ccr/svn/wst/trunk

Обзор

“Web Service Toolkit” состоит из двух частей :

На основе файла описания интерфейса (файл WSDL или файл pascal, описывающий веб-сервис) «ws_helper» (или мастера импорта файлов WSDL в самом Lazarus) создаст модуль объектного Паскаля, содержащего прокси, который и реализует этот интерфейс. Во время выполнения, когда происходит вызов работы веб сервиса, роль прокси модуля заключается в следующем:

Таким образом, прокси модуль позаботится о деталях [или скроет их] реализации SOAP.

Пример

Мы будем использовать пример «сервиса пользователя» («user service»), который поставляется с WST. Схема, описывающая службу, находится в папке \samples, файл - user_service_intf.wsdl. Чтобы использовать эту службу, мы должны перевести открытый интерфейс службы, который представен Языком описания веб-сервисов (т.е. WSDL) на язык Object Pascal. Нам потребуется скомпилировать и запустить сервер, предоставляющий этот сервис. Чтобы сделать это, пожалуйста, скомпилируйте проект, который находится в \samples\http_server, затем запустите полученную программу, чтобы в реальности запустить данный сервис; Обратите внимание, что в последних выпусках ОС Microsoft Windows Вам также может потребоваться включить службу для обеспечения правильной работы в сети. Чтобы перевести определения службы на язык Object Pascal в Lazarus мы можем использовать "мастер" импорта; мы можем также использовать 'ws_helper' как отдельную программу.

Мастер “импорта WSDL”

Этот мастер содержится в пакете wst_design.lpk, расположенном в каталоге \ide\lazarus данного инструментария (WST). После установки пакета в меню «Проект» в Lazarus добавляется раздел «Web Services Toolkit» («Инструментарий веб служб») с двумя подменю:

При клике на первом пункте меню (Import WSDL file...) вызывается диалоговое окно. Мы указываем WSDL файл и папку, в которой будут сохраняться сгенерированные файлы и нажимаем кнопку OK для завершения.

Мастер генерирует два файла:

Импорт при помощи ws_helper.

Программа ws_helper это версия для командной строки программы-мастера импорта. Для того, чтобы представить его возможности, ниже дан вывод утилиты с описанием ключей командной строки.

pascal
ws_helper [-uMODE] [-gOPTION] [-p] [-b] [-i] [-w] [-x] [-y] [-d] -[fSPECIFACTIONS] [-oPATH] [-aPATH] inputFilename
  -u MODE Generate the pascal translation of the WSDL input file 
    MODE value may be U for used types or A for all types
  -g  Code generation option, with the following options : 
    A  : object arrays are generated as "array" derived from TBaseObjectArrayRemotable
    C  : object arrays are generated as "collection" derived from TObjectCollectionRemotable
    EP : enum type's items are prefixed with the enum name
    EN : enum type's items are not prefixed with the enum name, the default
  -p  Generate service proxy
  -b  Generate service binder
  -i  Generate service minimal implementation. This will erase any existing implementation file!
  -o  PATH  Relative output directory
  -a  PATH  Absolute output directory
  -w  Generate WSDL file; Can be used to get wsdl from pascal
  -x  Generate XSD file; Can be used to get xsd from pascal
  -y  Generate easy access interface for wrapped parameters
  -d  Generate documentation as comment in the interface file
  -c  Indicate the parser's case sensitivity : 
    S  : the paser is case sensitive
    I  : the paser is not case sensitive
  -f  Specify unit(s) renaming option : oldName= NewName(;oldName= NewName)*

Для перевода user service файла WDSL примера запустите нижеследующую команду:

pascal
ws_helper.exe -uA -p -o. user_service_intf.wsdl
ws_helper, Web Service Toolkit 0.6 Copyright (c) 2006-2014 by Inoussa OUEDRAOGO
pascal
Parsing the file : user_service_intf.wsdl
(...)
Interface file generation...
Proxy file generation...
Metadata file generation...
File "user_service_intf.wsdl" parsed succesfully.

ниже показан вывод обоих файлов:

pascal
unit user_service_intf;
(...)
interface

uses SysUtils, Classes, TypInfo, base_service_intf, service_intf;

const
  sNAME_SPACE = 'urn:UserService';
  sUNIT_NAME = 'user_service_intf';

type

  TUserArray = class;
  TUser = class;
  TNote = class;  
(...)
  UserService = interface(IInvokable)
    ['{7537A085-DCD1-4B24-8B74-BC35ACB4896D}']
    function GetList():TUserArray;
    procedure Add(
      const  AUser : TUser
    );
    procedure Update(
      const  AUser : TUser
    );
    function Find(
      const  AName : string
    ):TUser;
    function Delete(
      const  AName : string
    ):boolean;
  end;

  procedure Register_user_service_intf_ServiceMetadata();
pascal
Unit user_service_intf_proxy;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
Interface

Uses SysUtils, Classes, TypInfo, base_service_intf, service_intf, user_service_intf;

Type

  TUserService_Proxy=class(TBaseProxy,UserService)
  Protected
    class function GetServiceType() : PTypeInfo;override;
    function GetList():TUserArray;
    procedure Add(
      const  AUser : TUser
    );
    procedure Update(
      const  AUser : TUser
    );
    function Find(
      const  AName : string
    ):TUser;
    function Delete(
      const  AName : string
    ):boolean;
  End;

  Function wst_CreateInstance_UserService(const AFormat : string = 'SOAP:'; const ATransport : string = 'HTTP:'; const AAddress : string = ''):UserService;

Implementation
uses wst_resources_imp, metadata_repository;

Function wst_CreateInstance_UserService(const AFormat : string; const ATransport : string; const AAddress : string):UserService;
Var
  locAdr : string;
Begin
  locAdr := AAddress;
  if ( locAdr = '' ) then
    locAdr := GetServiceDefaultAddress(TypeInfo(UserService));
  Result := TUserService_Proxy.Create('UserService',AFormat+GetServiceDefaultFormatProperties(TypeInfo(UserService)),ATransport + 'address=' + locAdr);
End;

function TUserService_Proxy.GetList():TUserArray;
Var
  locSerializer : IFormatterClient;
  locCallContext : ICallContext;
  locStrPrmName : string;
Begin
  locCallContext := Self as ICallContext;
  locSerializer := GetSerializer();
  Try
    locSerializer.BeginCall('GetList', GetTarget(),locCallContext);
    locSerializer.EndCall();

    MakeCall();

    locSerializer.BeginCallRead(locCallContext);
      TObject(Result) := Nil;
      locStrPrmName := 'result';
      locSerializer.Get(TypeInfo(TUserArray), locStrPrmName, Result);

  Finally
    locSerializer.Clear();
  End;
End;
(...)

Теперь мы готовы собрать простую программу для этого сервиса. Это простая клиентская программа вызывает метод GetList данного сервиса, чтобы получить список пользователей, которые уже зарегистрированы в этой службе; Список в этом случае выводится на экран:

pascal
program user_client_console;
uses SysUtils, fpc_http_protocol, soap_formatter,
     user_service_intf_proxy, user_service_intf;

var
  locService : UserService;
  items : TUserArray;
  item : TUser;
  i : Integer;
begin
  FPC_RegisterHTTP_Transport();
  locService := wst_CreateInstance_UserService();
  items := locService.GetList();
  try
    WriteLn('WST User Service Sample',sLineBreak);
    if (items.Length = 0) then
      WriteLn('  No user found.')
    else
      WriteLn(Format('  %d user(s) found : ',[items.Length]));
    for i := 0 to items.Length - 1 do begin
      item := items[i];
      WriteLn(Format('    Name= "%s", e-Mail= "%s"',[item.UserName,item.eMail]));
    end;
  finally
    items.Free();
  end;
end.

Модули base_service_intf, service_intf, soap_formatter, fpc_http_protocol, wst_resources_imp and metadata_repository предоставлены wst; Ниже результат выполнения:

pascal
WST User Service Sample
pascal
2 user(s) found :
  Name= "Lazarus FreePascal", e-Mail= "Lazarus@FreePascal.wst"
  Name= "Inoussa OUEDRAOGO", e-Mail= "sample@example.wst"

Найдено два пользователя. Функция wst_CreateInstance_UserService(), расположенная в файле user_service_intf.pas, создает экземпляр прокси, основанная на информации службы, содержащейся в файле WSDL. Полностью исходный код примера поставляется вместе с инструментарием WST библиотеки в папке \samples\user_client_console; Обратите внимание, что пример, представленной в этой папке более полный, чем приведенный на этой странице

Параметры соединения

Основной формат:

pascal
protocol:paramName=paramValue(;paramName=paramValue)*

Параметры HTTP прокси

Для HTTP поддерживаемые параметры приведены ниже:

HTTP соединение через прокси поддерживается. Ниже пример адресной строки.

pascal
  const
    sADDRESS = 'http:address=http://webservices.amazon.com/AWSECommerceService/2007-04-04'+
                 ';ProxyServer=197.150.10.10;ProxyPort=9881'+
                 ';ProxyUsername=inoussa;ProxyPassword=wst';

Инструментарий имеет 3 HTTP реализации, основанная на встроеной реализации FPC (fpc_http_server.pas), Synapse ( synapse_http_protocol.pas ) и Indy (indy_http_protocol.pas).

Параметры TCP соединения

Поддерживаемые TCP параметры:

Ниже приведена примерная адресная строка

pascal
  Const
    sADDRESS = 'TCP:Address=10.0.0.3;Port=1234;target=UserService';

Инструментарий имеет 3 TCP реализации, основанные на встроенной реализации в FPC (fpc_tcp_server.pas), Synapse (synapse_tcp_protocol.pas) и Indy (indy_tcp_protocol.pas).

Параметры соединения через библиотеки (LIB)

Идея этого протокола в том, чтобы иметь возможность подключаться к службам через динамические библиотеки (DLL/DSO). Это может рассматриваться как плагин фреймворк, где плагины (службы) обеспечены динамическими библиотеками.

Параметры соединения LIB:

Ниже пример адресной строки

pascal
  Const
    sADDRESS = 'LIB:FileName=..\library_server\lib_server.dll;target=UserService';

Инструментарий имеет одну реализацию LIB соединения (library_protocol.pas).

Папка с примерами содержит 4 проекта user_client_console, tcp_server, http_server и library_server, которые демонстрируют протоколы TCP, HTTP и протокол типа "библиотека"

Параметры соединения с тем же процессом (SAME_PROCESS)

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

SAME_PROCESS поддерживает следующие параметры:

Ниже пример адресной строки

pascal
  Const
    sADDRESS = 'SAME_PROCESS:Address=UserService';

Инструментарий имеют одну реализацию (same_process_protocol.pas).

Мульти-адресный сервис (Адрес на операцию)

Некоторые сервисы (как пример, eBay SOAP сервисы) используют отдельный адрес на каждую операцию. Инструментарий использует расширенные мета данные (см. главу о мета данных сервисов) для установки адресов операций. “Ebay” SOAP пример, расположенный в tests\ebay папке демонстрирует настройку адреса операции.

Решение проблем

'Сообщение об ошибке: Invalid parameter : "AProtocolData"'

Это случается, когда неизвестно какой протокол следует использовать. Убедитесь, что правильный форматтер выбран (к примеру: 'SOAP:') и что указанные форматтер зарегистрирован. Это делается добавлением соответствующего модуля xxx_formatter в Ваш блок uses. (для примера: soap_formatter)

Сторона сервера (создание сервиса)

Обзор

Инструментарий веб-сервиса содержит фреймворк для серверной стороны и редактор библиотеки типов на базе языка WSDL для создания сервиса. Ключевые особенности:

Пример

Для того, чтобы создать сервис,Вы должны:

Описание Интерфейса (Interface) сервиса

Начиная с версии 0.5, WST обеспечивает редактор библиотеки типов WSDL для описания типов и сервисов, используемых при реализации. Рисунок (3) ниже представляет основной интерфейс этого инструмента. Редактор библиотеки типов представлен как

Особенности Редактора Библиотеки Типов:

Ниже приведен пример на изображении. Мы будем использовать файл user_service_intf.wsdl, расположенный в \samples directory для нашего примера.

Экспорт файлов Pascal.

Редактор Библиотеки Типов имеет возможность генерировать Pascal версию файла WSDL. Чтобы сделать это, следует кликнуть “Files\Save generated files ...” (это также может быть сделано через контекстное меню); Это вызовет диалоговое окошко как показано в изображении ниже. Редактор Библиотеки Типов, экспорт в файлы Pascal.

Нажмите OK для завершения. Программа ws_helper имеет те же возможности и файлы могут сгенерированы с помощью нижеследующей команды:

pascal
ws_helper\ws_helper.exe -i -b -o. user_service_intf.wsdl
ws_helper, Web Service Toolkit 0.5 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
Parsing the file : user_service_intf.wsdl
Information : Parsing "tns:TUserArray" ...
Information : Parsing "tns:TUser" ...
Information : Parsing "tns:TUser" ...
Information : Parsing "xsd:string" ...
Information : Parsing "tns:TUser" ...
Information : Parsing "xsd:string" ...
Information : Parsing "xsd:boolean" ...
Information : Parsing "TUserArray" ...
Information : Parsing "TUser" ...
Information : Parsing "TUserCategory" ...
Information : Parsing "TNote" ...
Interface file generation...
Proxy file generation...
Metadata file generation...
File "user_service_intf.wsdl" parsed succesfully.

Полностью проект расположен в папке “samples”. Ниже приведен вывод сгенерированных файлов интерфейса.

pascal
unit user_service_intf;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
interface

uses SysUtils, Classes, TypInfo, base_service_intf, service_intf;

const
  sNAME_SPACE = 'urn:UserService';
  sUNIT_NAME = 'user_service_intf';

type

  TUser = class;
  TUserArray = class;

  TUserCategory = ( 
    Normal
    ,Admin
  );

  TUser = class(TBaseComplexRemotable)
  published
    property Category : TUserCategory read FCategory write FCategory;
    property UserName : string read FUserName write FUserName;
    property eMail : string read FeMail write FeMail;
    property Preferences : string read FPreferences write FPreferences;
    property Note : TNote read FNote write FNote;
  end;

  TUserArray = class(TBaseObjectArrayRemotable)
  private
    function GetItem(AIndex: Integer): TUser;
  public
    class function GetItemClass():TBaseRemotableClass;override;
    property Item[AIndex:Integer] : TUser Read GetItem;Default;
  end;

  UserService = interface(IInvokable)
    ['{CA6F6192-C3DE-4D9C-B3DF-E616376A0DC9}']
    function GetList():TUserArray;
    procedure Add(
      Const AUser : TUser
    );
    procedure Update(
      Const AUser : TUser
    );
    function Find(
      Const AName : string
    ):TUser;
    function Delete(
      Const AName : string
    ):boolean;
  end;
(...)

Обеспечение реализации сервиса

Модуль user_service_intf_imp.pas, сгенерированный выше содержит скелет реализации класса для интерфейса. Это определяет процедуру, названную RegisterUserServiceImplementationFactory. Процедура регистрирует класс как провайдер реализации сервиса в реестре реализаций.

pascal
Unit user_service_intf_imp;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
Interface
Uses SysUtils, Classes, 
     base_service_intf, server_service_intf, server_service_imputils,
     user_service_intf, cursor_intf;

Type
  { TUserService_ServiceImp }

  TUserService_ServiceImp=class(TBaseServiceImplementation,UserService)
  Protected
    function GetList():TUserArray;
    procedure Add(
      Const AUser : TUser
    );
    procedure Update(
      Const AUser : TUser
    );
    function Find(
      Const AName : string
    ):TUser;
    function Delete(
      Const AName : string
    ):boolean;
  End;

  procedure RegisterUserServiceImplementationFactory();

Implementation
(...)

procedure TUserService_ServiceImp.Add(Const AUser : TUser);
var
  locObj : TUser;
Begin
  locObj := Find(AUser.UserName);
  if ( locObj <> nil ) then
    raise Exception.CreateFmt('Duplicated user : "%s"',[AUser.UserName]);
  locObj := TUser.Create();
  locObj.Assign(AUser);
  FUserList.Add(locObj);
End; 

procedure RegisterUserServiceImplementationFactory();
Begin
  GetServiceImplementationRegistry().Register(
    'UserService',
TImplementationFactory.Create(TUserService_ServiceImp) as IServiceImplementationFactory);
End;

(...)

“ws_helper” имеет возможность генерировать прокси файл, файл скелета реализации и биндер файл (файл связки) (см. листинг ниже).

pascal
ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
ws_helper [-uMODE] [-p] [-b] [-i] [-oPATH] inputFilename
  -u MODE Generate the pascal translation of the WSDL input file
       MODE value may be U for used types or A for all types
  -p  Generate service proxy
  -b  Generate service binder
  -i  Generate service minimal implementation
  -o  PATH  Relative output directory
  -a  PATH  Absolute output directory

Начало файла реализации может также создать, используя ws_helper с ключами -i и -b как упомянуто выше;

pascal
ws_helper\ws_helper.exe -i -b -o. user_service_intf.wsdl
ws_helper, Web Service Toolkit 0.4 Copyright (c) 2006, 2007 by Inoussa OUEDRAOGO
Parsing the file : user_service_intf.wsdl
Proxy file generation...
Binder file generation...
Implementation file generation...
Metadata file generation...
File "user_service_intf.wsdl" parsed succesfully..

Обеспечение биндера для веб-сервиса

Роль биндера (связки) в следующем:

Модуль user_service_intf_binder.pas, сгенерированный выше, содержит:

Нижеследующий код вывода показывает интерфейсную часть модуля и обработчик метода биндера.

pascal
unit user_service_intf_binder;
{$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF}
interface
uses SysUtils, Classes, base_service_intf, server_service_intf, user_service_intf;

type
  TUserService_ServiceBinder=class(TBaseServiceBinder)
  Protected
    procedure GetListHandler(AFormatter:IFormatterResponse);
    procedure AddHandler(AFormatter:IFormatterResponse);
    procedure UpdateHandler(AFormatter:IFormatterResponse);
    procedure FindHandler(AFormatter:IFormatterResponse);
    procedure DeleteHandler(AFormatter:IFormatterResponse);
  Public
    constructor Create();
  End;

  TUserService_ServiceBinderFactory = class(TInterfacedObject,IItemFactory)
  protected
    function CreateInstance():IInterface;
  End;

  procedure Server_service_RegisterUserServiceService();

(...)

procedure TUserService_ServiceBinder.AddHandler(AFormatter:IFormatterResponse);
Var
  cllCntrl : ICallControl;
  tmpObj : UserService;
  callCtx : ICallContext;
  strPrmName : string;
  procName,trgName : string;
  AUser : TUser;
Begin
  callCtx := GetCallContext();
  TObject(AUser) := Nil;
  
  strPrmName := 'AUser';  AFormatter.Get(TypeInfo(TUser),strPrmName,AUser);
  If Assigned(Pointer(AUser)) Then
    callCtx.AddObjectToFree(TObject(AUser));
  
  tmpObj := Self.GetFactory().CreateInstance() as UserService;
  if Supports(tmpObj,ICallControl,cllCntrl) then
    cllCntrl.SetCallContext(GetCallContext());
  
  tmpObj.Add(AUser);
  
  procName := AFormatter.GetCallProcedureName();
  trgName := AFormatter.GetCallTarget();
  AFormatter.Clear();
  AFormatter.BeginCallResponse(procName,trgName);
  AFormatter.EndCallResponse();
  
  callCtx := Nil;
End;

Host the service into an application server.

The application server's role is to route incoming service requests to the Web Service Toolkit runtime. For the runtime to process service requests :

The runtime interface is defined in the server_service_intf unit. This unit contains :

Starting from the version 0.5, the toolkit provides a simplified model to develop applications server . This is achieved using the listener classes. A listener implements a transport between the server and its clients. The toolkit provides two (2) HTTP listeners implementations:

Three (3) TCP listeners implementations are provided:

All listeners derive from TwstListener defined in the server_listener.pas file.

Below is printed an HTTP server sample. The code is divided into three (3) parts :

pascal
  Server_service_RegisterSoapFormat(); 
  Server_service_RegisterXmlRpcFormat();
  Server_service_RegisterBinaryFormat() ;
pascal
  RegisterUserServiceImplementationFactory();
  Server_service_RegisterUserServiceService();
pascal
  AppObject  := TwstFPHttpListener.Create();
  AppObject.Start();

Complete listing :

pascal
program http_server;
{$mode objfpc}{$H+}
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, SysUtils,
  fpc_http_server, metadata_service, logger_extension, server_listener,
  server_service_soap, server_binary_formatter, server_service_xmlrpc, config_objects,
  user_service_intf, user_service_intf_binder, user_service_intf_imp;
var
  AppObject : TwstListener;
begin
  Server_service_RegisterBinaryFormat();
  Server_service_RegisterSoapFormat();
  Server_service_RegisterXmlRpcFormat();

  RegisterUserServiceImplementationFactory();
  Server_service_RegisterUserServiceService();
  AppObject := TwstFPHttpListener.Create();
  try
    WriteLn('"Web Service Toolkit" HTTP Server sample listening at:');
    WriteLn('');
    WriteLn('http://127.0.0.1:8000/');
    WriteLn('');
    WriteLn('Press enter to quit.');
    AppObject.Start();
    ReadLn();
  finally
    FreeAndNil(AppObject);
  end;
end.

Server_service_RegisterUserServiceService located in the user_service_intf_binder unit ( generated by ws_helper ) registers the UserService service by calling in turn GetServerServiceRegistry:

pascal
procedure Server_service_RegisterUserServiceService();
Begin
  GetServerServiceRegistry().Register(
    'UserService',TUserService_ServiceBinderFactory.Create() as IitemFactory
  );
End;

Server_service_RegisterSoapFormat located in the server_service_soap unit ( provided by the toolkit ) registers the SOAP implementation by calling in turn GetFormatterRegistry:

pascal
  procedure Server_service_RegisterSoapFormat();
  begin
    GetFormatterRegistry().Register(
      sPROTOCOL_NAME,
      sSOAP_CONTENT_TYPE,
      TSimpleItemFactory.Create(TSOAPFormatter) as IitemFactory
    );
    RegisterStdTypes();
  end;

Server_service_RegisterBinaryFormat located in the server_binary_formatter unit ( provided by the toolkit ) registers the Binary message implementation by calling in turn GetFormatterRegistry:

pascal
  procedure Server_service_RegisterBinaryFormat();
  begin
    GetFormatterRegistry().Register(
      sPROTOCOL_NAME,
      sBINARY_CONTENT_TYPE,
      TBinaryFormatterFactory.Create() as IitemFactory
    );
  end;

Server_service_RegisterXmlRpcFormat located in the server_service_xmlrpc unit ( provided by the toolkit ) registers the XMLRPC message implementation by calling in turn GetFormatterRegistry:

pascal
  procedure Server_service_RegisterXmlRpcFormat();
  begin
    GetFormatterRegistry().Register(
      sPROTOCOL_NAME,
      sXMLRPC_CONTENT_TYPE,
      TSimpleItemFactory.Create(TXmlRpcFormatter) as IItemFactory
    );
  end;

In order to give it a try one have to :

  1. compile the server ( \samples\tcp_server\tcp_server.lpi it is a console program),
  2. compile the client application ( \samples\user_client_console\user_client_console.lpi ),
  3. execute the server and start listening,
  4. execute the client.

WSDL generation

Services in the toolkit are organized into meta data repositories. Conceptually a repository corresponds :

The repository is the toolkit WSDL generation unit.

The Metadata Service

The toolkit is provided with an easy to use metadata service implementation which in turn uses the raw interface defined in the metadata_repository unit (see above). A Lazarus GUI client application is located in the tests\metadata_browser folder.

WSDL generation API

The metadata_wsdl pascal unit contains the GenerateWSDL function for WSDL generation from a repository (see the signature below).

pascal
  PServiceRepository = ^TServiceRepository;
  TServiceRepository = record
    NameSpace        : ShortString;
    Name             : ShortString;
    RootAddress      : ShortString;
    ServicesCount    : Byte;
    Services         : PService;
  end;

  procedure GenerateWSDL(AMdtdRep : PServiceRepository; ADoc : TDOMDocument);
WSDL Customization

The WSDL generation is based on the IWsdlTypeHandler and the IWsdlTypeHandlerRegistry interfaces located in the metadata_wsdl unit. In order to customize the generated WSDL, one has to provide a class implementing the IWsdlTypeHandler interface. Then that class has to be registered in the registry. The metadata_wsdl unit contains implementations for pascal enumerations, TBaseComplexRemotable descendants, and TBaseArrayRemotable descendants.

Sample

A functional sample project is located under \samples\http_server . It is an Indy base http server.

Расширения сервисов

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

pascal
  TMessageStage = (
    msAfterDeserialize, msAfterSerialize, msBeforeDeserialize, msBeforeSerialize
  );

  IServiceExtension = interface
    ['{E192E6B3-7932-4D44-A8AC-135D7A0B8C93}']
    procedure ProcessMessage(
      const AMessageStage     : TMessageStage;
               ACallContext   : ICallContext;
               AMsgData       : IInterface
    );
  end;

Фактический тип параметра AMsgData зависит от стадии обработки сообщения и соответствует:

Эти типы расположены в модуле server_service_intf. Расширения должны быть зарегистрированы в реестре расширений (расположенном в модуле server_service_intf), приведенном ниже

pascal
  IServiceExtensionRegistry = Interface
    ['{68DC78F1-E6CF-4D6B-8473-75288794769C}']
    function Find(const AName : string):IServiceExtension;
    procedure Register(
      const AName    : string;
            AFactory : IItemFactory
    );
  end;

Чтобы реализация сервиса могла использовать расширение сервиса, она должна зарегистрироваться в этом расширении. С этой целью Интерфейс IServiceImplementationFactory предоставляет метод RegisterExtension. Код примера полностью представлен в \samples\http_server (реализуется в \samples\logger_extension.pas).

Метаданные служб

Службы в инструментарии организованы в ***репозитории мета данных (смотрите “Метаданные служб” ниже)***. Концептуально репозитории соответствуют:

Инструмент ws_helper, когда парсит файл описания интерфейса, записывает мета данные служб, содержащихся в файле в ресурсный файл Lazarus. Ресурсный файл в этом случай встроен в сгенерированный файл модуля биндера (смотрите часть “initialization” модуля). Во время выполнения мета данных службы доступны через интерфейс IModuleMetadataMngr, определенный в модуле metadata_repository (смотрите ниже). Функция GetModuleMetadataMngr, определенная в том же модуле, возвращает экземпляр объекта, поддерживающего этот интерфейс.

pascal
  IModuleMetadataMngr = interface
    ['{B10ACF6A-A599-45A3-B083-BEEFB810C889}']
    function IndexOfName(const ARepName : shortstring):Integer;
    function GetCount():Integer;
    function GetRepositoryName(const AIndex : Integer):shortstring;
    procedure SetRepositoryNameSpace(const ARepName,ANameSpace : shortstring);
    function LoadRepositoryName(
      const ARepName,ARootAddress  : shortstring;
      out   ARepository  : PServiceRepository
    ):Integer;
    procedure ClearRepository(var ARepository : PServiceRepository);
    procedure SetServiceCustomData(
      const ARepName       : shortstring;
      const AServiceName   : shortstring;
      const ADataName,
            AData          : string
    );
    procedure SetOperationCustomData(
      const ARepName       : shortstring;
      const AServiceName   : shortstring;
      const AOperationName : shortstring;
      const ADataName,
            AData          : string
    );
    function GetServiceMetadata(const ARepName,AServiceName : shortstring) : PService;
    procedure ClearServiceMetadata(var AService : PService);
  end;

Расширенные мета данные

Интерфейс мета данных обеспечивает способ для добавления пользовательских данных в уже записанные. Метаданные служб могут быть установлены через SetServiceCustomData, а операции метаданных могут быть установлен через метод SetOperationCustomData. Расширенные метаданных репозитория должны быть зарегистрированы после регистрации метаданных сервиса, записанных в файле ресурса. ТАким образом, для клиентского приложения сгенерированный прокси модуль содержит условный фрагмент кода для вызова процедуры регистрации как показано ниже с примером eBay, расположенном в папке tests\ebay. Имя процедуры получены из имени интерфейса модуля (имя репозитория): Register_%UNIT_NAME%_ServiceMetadata.

pascal
  initialization
    {$i ebay.lrs}

    {$IF DECLARED(Register_ebay_ServiceMetadata)}
    Register_ebay_ServiceMetadata();
    {$ENDIF}
  End.

Поддержка заголовков (Headers)

Класс THeaderBlock

pascal
  THeaderBlock = class(TBaseComplexRemotable)
  public
    property Direction : THeaderDirection read FDirection write FDirection;
    property Understood : Boolean read FUnderstood write FUnderstood;
  published
    property mustUnderstand : Integer read FmustUnderstand write SetmustUnderstand 
      stored HasmustUnderstand;
  end;

Показанный выше заголовок THeaderBlock (часть private была пропущена для краткости), расположенный в модуле base_service_intf, корневной класс для всех классов заголовков, наследуемых от него. Свойство Direction показывает является ли заголовок входящим или исходящим. Свойство mustUnderstand определяется является ли оно обязательным.

Определение классов заголовков

Заголовки SOAP наследуются от базового класса THeaderBlock, расположенного в модуле base_service_intf. Они регистрируются в реестре типов. Ниже воспроизведен пример заголовка, извлеченного и примера проекта "Калькулятор".

pascal
  TCalcHeader = class(THeaderBlock)
  published
    property Login : string read FLogin write FLogin;
    property Password : string read FPassword write FPassword;
    property WantedPrecision : Integer read FWantedPrecision write FWantedPrecision;
  end;

Интерфейс ICallContext

pascal
  ICallContext = Interface
    ['{855EB8E2-0700-45B1-B852-2101023200E0}']
    procedure AddObjectToFree(const AObject : TObject);
    procedure Clear();
    function AddHeader(
      const AHeader        : THeaderBlock;
      const AKeepOwnership : Boolean
    ):Integer;overload;
    function AddHeader(
      const AHeader        : TBaseRemotable;
      const AKeepOwnership : Boolean;
      const AName          : string = ''
    ):Integer;overload;
    function GetHeaderCount(const ADirections : THeaderDirections):Integer;
    function GetHeader(const AIndex : Integer) : THeaderBlock;
    procedure ClearHeaders(const ADirection : THeaderDirection);
  End;

Интерфейс ICallContext, определенный в модуле base_service_intf представляет контекст вызова сервиса. Метод AddHeader позволяет отправлять заголовки, тогда как метод GetHeader извлекает заголовок в контексте вызова.

Заголовки клиентской стороны

Ссылка ICallContext может быть получена из текущего экземпляра прокси просто запросив его для этого интерфейса как показано в фрагменте коде ниже, взятого из примера проекта-клиента "калькулятор".

pascal
  var
    ch : TCalcHeader;
    hdrs : ICallContext;
  begin
    FObj := TCalculator_Proxy.Create('Calculator', edtFormat.Text, edtAddress.Text);

    ch := TCalcHeader.Create();
    ch.mustUnderstand := 1;
    ch.Login := 'azerty';
    ch.Password := 'qwerty';
    ch.WantedPrecision := 1210;
    hdrs := FObj as ICallContext;
    hdrs.AddHeader(ch,true);

Заголовок может быть сделан обязательным установкой его свойства mustUnderstand в 1 как показано в коде выше.

Server side headers

The ICallControl interface

pascal
  ICallControl = interface
    ['{7B4B7192-EE96-4B52-92C7-AE855FBC31E7}']
    procedure SetCallContext(ACallContext : ICallContext);
    function GetCallContext():ICallContext;
  end;

The ICallControl interface, located in the server_service_intf unit, is used by the toolkit runtime to share the executing call environment with service implementation classes. When the runtime is about to issue a call against a implementation class instance, it queries that instance for ICallControl interface support; If the implementation has ICallControl interface support then the obtained reference is used to set the call context through the SetCallContext method. The implementation instance can then access the call context by calling the GetCallContex method. The toolkit provides the TBaseServiceImplementation class which has support for the ICallControl interface and can be used as a base implementation class. It is the base class used by the ws_helper generated skeleton implementation class when invoked with the -i command line option. The method printed below, extracted from the calculator sample service demonstrates the access to headers for read and write.

pascal
  function TCalculator_ServiceImp.AddInt(
    Const A : Integer;
    Const B : Integer
  ):TBinaryArgsResult;
  var
    hdr : TCalcResultHeader;
    h : TCalcHeader;
    cc : ICallContext;
  Begin
    hdr := TCalcResultHeader.Create();
    cc := GetCallContext();
    if Assigned(cc) and ( cc.GetHeaderCount([hdIn]) > 0 ) and ( cc.GetHeader(0).InheritsFrom(TCalcHeader) ) then begin
      h := cc.GetHeader(0) as TCalcHeader;
      h.Understood := True;
      hdr.Assign(h);
    end;
    hdr.TimeStamp := DateTimeToStr(Now());
    hdr.SessionID := 'testSession';
    cc.AddHeader(hdr,True);
    hdr := nil;
    Result := TBinaryArgsResult.Create();
    Try
      Result.Arg_OP := '+';
      Result.Arg_OpEnum := coAdd;
      Result.Arg_A := A;
      Result.Arg_B := B;
      Result.Arg_R := A + B;
      Result.Comment := 'Doing an + operation';
    Except
      FreeAndNil(Result);
      Raise;
    End;
  End;

Headers that does not derived from THeaderBlock.

Classes that inherit from TBaseRemotable can also be used as headers; In that case a THeaderBlockProxy’s instance is automatically created and used as a wrapper to allow a TBaseRemotable instance to be sent and received as a header block. The ICallContext has an overloaded AddHeader method that accepts TBaseRemotable’s instances.

Специфика SOAP

Стиль связывания

Стиль связывания используется для определения того, что сервис - RPC-ориентированный или Document-ориентированный.

Клиентская часть

Стиль связывания может быть прописан в строке SOAP протокола при создании прокси сервиса. Значение по умолчанию для стиля связывания RPC. Ниже показан пример, демонстрирующий использования Document-стиля.

pascal
  locService := TSampleService_Proxy.Create(
                  'SampleService',
                  'SOAP:Style=Document;EncodingStyle=Literal',
                  'http:Address=http://127.0.0.1/services/SampleService'
                );

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

На настоящий момент сервисы, созданные в инструментарии используют стиль связывания RPC

Стиль кодирования

Стиль кодирования показывает правила, используемые для кодирования типов в XML. Поддерживаемые значения Encoded и Literal.

Клиентская часть

Стиль кодирования может быть указан с помощью строки протокола SOAP при создании прокси сервиса. Значение по умолчанию для стиля кодирования - Encoded. Вышеприведенный пример демонстрирует использования стиля Literal.

Серверная часть

На настоящий момент сервисы, который создаются с помощью инструментария используют стиль кодирования Encoded.

Приведенные примеры

Примеры, расположенные в папке “tests”.

Примеры на стороне клиента ( протестировано на Windows XP, Ubuntu и MacOS на PowerPC)

Примеры со стороны сервера

Статус

Инструментарий может использоваться как для простых типов, так и для типов классов. Сериализация предназначена для настройки базовых типов и типов классов путем реализации классов, производных от “TBaseRemotable”. Эти классы должны быть зарегистрированы в реестре типа.

Сериализация

Сериализация основана на интерфейсе IFormatterBase, расположенном в модуле base_service_sintf.

Инструментарий содержит четыре реализации сериализаторов: сериализатор SOAP, сериализатор XMLRPC, сериализатор JSONRPC и бинарный сериализатор. Этот сериализатор был протестирован на FPC 2.x и Delphi 7.

сериализатор SOAP

Сериализатор SOAP реализует SOAP 1.1. Он поддерживает следующие типы Pascal:

Бинарный сериализатор

Бинарный сериализатор более оптимизирован по скорости и размеру в сравнении с SOAP сериализацией. Используется big-endian для потоковой передачи данных. Имеет поддержку нижеприведенных паскалевских типов:

Сериализация типа класса

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

Корневой класс “TBaseRemotable”
pascal
  TBaseRemotable = class(TPersistent)
  Public
    constructor Create();virtual;
    class procedure Save(
            AObject   : TBaseRemotable;
            AStore    : IFormatterBase;
      Const AName     : String;
      Const ATypeInfo : PTypeInfo
    );virtual;abstract;
    class procedure Load(
      Var   AObject   : TObject;
            AStore    : IFormatterBase;
      var   AName     : String;
      const ATypeInfo : PTypeInfo
    );virtual;abstract;
  End;

TBaseRemotable это абстрактный базовый класс, используемый интерфейсом форматтера для возможностей настройки сериализации. Этот класс определяется виртуальный конструктором/virtual constructor и, главным образом, двумя виртуальными абстрактными методами класса:

Сериализация “TBaseComplexRemotable”
pascal
  TBaseComplexRemotable = class(TAbstractComplexRemotable)
  public
    class procedure Save(
            AObject   : TBaseRemotable;
            AStore    : IFormatterBase;
      const AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
    class procedure Load(
      var   AObject   : TObject;
            AStore    : IFormatterBase;
      var   AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
    class procedure RegisterAttributeProperty(const AProperty : shortstring);virtual;
    class procedure RegisterAttributeProperties(const APropertList : array of shortstring);virtual;
    class function IsAttributeProperty(const AProperty : shortstring):Boolean;
    procedure Assign(Source: TPersistent); override;
  end;

TBaseComplexRemotable реализует сериализацию для наследников опубликованных ("published") свойств классов-наследников. Сериализация основана на информации типов времени выполнения ("runtime type information" RTTI) и может быть настроена для случаев:

Нижеприведенный класс демонстрирует пример настройки сериализации.

pascal
  TSampleClass = class(TBaseComplexRemotable)
  private
    FProp_Always: Integer;
    FProp_Never: Integer;
    FProp_Optional: Integer;
    function GetStoredProp_Optional: boolean;
  published
    //Это свойство всегда будет сериализоваться
    property Prop_Always : Integer read FProp_Always write FProp_Always;
    //Это свойство никогда не будет сериализоваться
    property Prop_Never : Integer read FProp_Never write FProp_Never stored False;
    //Это свойство будет сериализоваться, если "Self.GetStoredProp_Optional() = True"
    property Prop_Optional : Integer read FProp_Optional write FProp_Optional stored GetStoredProp_Optional;
  End;

TBaseComplexRemotable позволяет сериализацию свойств как атрибутов. Свойства должны быть зарегистрированы в качестве таковых с помощью метода класса RegisterAttributeProperty или метода RegisterAttributeProperties.

TBaseComplexRemotable

TBaseComplexSimpleContentRemotable обеспечивает реализацию комплексных типов “XML Схемы”, которые расширяют простые типы атрибутами. Нижеследующий пример иллюстрирет это:

xml
  <xs:complexType name="DecimalWithUnits">
    <xs:simpleContent>
      <xs:extension base="xs:decimal">
        <xs:attribute name="Units" type="xs:string"
                      use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

Этот тип будет приведен с помощью ws_helper к Pascal как

pascal
  DecimalWithUnits = class(TComplexFloatExtendedContentRemotable)
  private
    FUnits : string;
  published
    property Units : string read FUnits write FUnits;
  end;

использование предустановленных типов ( в base_service_intf.pas )

pascal
  TBaseComplexSimpleContentRemotable =
     class(TAbstractComplexRemotable)
  protected
    class procedure SaveValue(
      AObject : TBaseRemotable; 
      AStore : IformatterBase
    );virtual;abstract;
    class procedure LoadValue(
      var AObject : TObject; 
      AStore : IformatterBase
    );virtual;abstract;
  public
    class procedure Save(
            AObject   : TBaseRemotable;
            AStore    : IFormatterBase;
      const AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
    class procedure Load(
      var   AObject   : TObject;
            AStore    : IFormatterBase;
      var   AName     : string;
      const ATypeInfo : PTypeInfo
    );override;
  end;

  TComplexFloatExtendedContentRemotable =
     class(TBaseComplexSimpleContentRemotable)
  private
    FValue: Extended;
  protected
    class procedure SaveValue(
      AObject : TBaseRemotable; 
      AStore : IformatterBase
    );override;
    class procedure LoadValue(
       var AObject : TObject; 
       AStore : IformatterBase
    );override;
  public
    property Value : Extended read FValue write FValue;
  end;

An instance of this type looks like the one below. Every attribute must be registered using the RegisterAttributeProperty() method. The toolkit provides class for Pascal basic types( TComplexInt8UContentRemotable, TComplexInt8SContentRemotable, TComplexInt16SContentRemotable, ...).

xml
  <example Units = "meter">
    12.10
  </example>
Provided array implementations

The toolkit provides array implementation for basic types ( in the base_service_intf unit ) listed below. The implementations are based on the serialization's customization.

The toolkit array's implementation support “embedded” array serialization. This type of array occurs typically with types like the following one ( the "ResponseGroup" may be repeated ):

xml
  <xs:complexType name="CustomerContentSearchRequest">
    <xs:sequence>
      <xs:element name="CustomerPage" type="xs:positiveInteger"
         minOccurs="0"/>
      <xs:element name="Email" type="xs:string" minOccurs="0"/>
      <xs:element name="Name" type="xs:string" minOccurs="0"/>
      <xs:element name="ResponseGroup" type="xs:string"
           minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

which could be instantiated as

xml
  <search>
    <CustomerPage> 1 </CustomerPage>
    <Name>Sample name</Name>
    <ResponseGroup>Group 1</ResponseGroup>
    <ResponseGroup>Group 2</ResponseGroup>
    <ResponseGroup>Group 3</ResponseGroup>
  </search>

This type will be translate to Pascal by ws_helper as (the private and protected parts are omitted to be short)

pascal
(...)
  CustomerContentSearchRequest_ResponseGroupArray =
    class(TBaseSimpleTypeArrayRemotable)
  public
    class function GetItemTypeInfo():PTypeInfo;override;
    procedure SetLength(const ANewSize : Integer);override;
    property Item[AIndex:Integer] : string 
      read GetItem write SetItem; default;
  end;

  CustomerContentSearchRequest = class(TBaseComplexRemotable)
  published
    property CustomerPage : positiveInteger 
       read FCustomerPage 
       write FCustomerPage stored HasCustomerPage;
    property Email : string 
       read FEmail 
       write FEmail 
       stored HasEmail;
    property Name : string read FName write FName stored HasName;
    property ResponseGroup :
       CustomerContentSearchRequest_ResponseGroupArray
       read FResponseGroup 
       write FResponseGroup;
  end;

implementation
(...)
  GetTypeRegistry().ItemByTypeInfo[
    TypeInfo(CustomerContentSearchRequest_ResponseGroupArray)]
    .RegisterExternalPropertyName(sARRAY_STYLE,sEmbedded);
(...)

The last instruction set the array style to “Embedded” and so the SOAP formatter will serialize the array accordingly.

Test cases

TODO

TODO Common

TODO Client-Side

TODO Server-Side

Extend the toolkit to Server side for :

]]

Лицензия

Весь код в WST под лицензией FPC' RTL (modified LGPL).

Загрузка и NewsGroup

FAQ

Вопросы и ответы по WST Web Service Toolkit:FAQ.

Журнал изменений

Version 0.7

Version 0.6

Version 0.5

Version 0.4

Version 0.3.1 ( 21 August 2006 )

Version 0.3 ( 5 August 2006 )

Version 0.2.3.2 ( 5 July 2006 )

Version 0.2.3 ( 4 July 2006 )

Version 0.2.2 ( 7 June 2006 )

Автор

Inoussa OUEDRAOGO, http://inoussa12.googlepages.com/

См. также


From the Free Pascal wiki, page "Web Service Toolkit/ru", as it stood on 2020-03-04 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.