Escolar Documentos
Profissional Documentos
Cultura Documentos
Desvendando Web Services / Delphi.
Desvendando Web Services / Delphi.
Resumo: Este artigo tem por objetivo dar uma visão geral e completa sobre uma das
melhores novidades do Delphi 6, o suporte a Web Services.
Visão geral
Os servidores podem descrever seus próprios serviços através da WSDL (Web Service
Definition Language). Dessa forma, os clientes podem facilmente obter informações
sobre os servidores que usarão, tais como: estrutura, métodos e parâmetros exigidos.
Isso se torna essencialmente útil quando se está codificando servidores que serão
usados por terceiros ou implementando clientes que usam serviços de outras
empresas.
No caso particular do Delphi podemos usar um wizard que importa essas informações e
cria automaticamente as units com as definições dos serviços ofertados pelo servidor.
SOAP/XML
Mais informações sobre SOAP podem ser encontradas no site da especificação oficial
em http://www.w3.org/TR/SOAP/.
Página 1 de 14
Desvendando Web Services (Soap/XML)
Daremos início , agora, à parte prática deste artigo, então para começar devemos criar
um novo projeto de Web Services. No meu caso escolhi CGI stand-alone como tipo da
aplicação e ela rodará diretamente no IIS5, mas estejam livres para escolherem o que
mais lhe convierem. Feito isso seremos apresentados a seguinte tela:
Interfaces Invokable
Antes de passarmos a definição, vamos ver alguma coisa a respeito delas. Por que
precisamos derivar nossas interfaces de IInvokable? A resposta é simples, a
arquitetura trazida pelo Delphi requer que as interfaces de definição sejam compiladas
com informação de run-time, e é isso que a que a IInvokable garante. A única
diferença entre IUnknow e IInvokable é que esta é compilada com a diretiva {$M},
fazendo gerar RTTI para ela e todas suas descendentes.
Página 2 de 14
Desvendando Web Services (Soap/XML)
Agora que vimos o conceito vamos passar para a parte prática. Vamos começar
definindo uma interface para as operações aritméticas básicas:
unit IMathIntf;
interface
type
IMath = interface(IInvokable)
['{E4F918D6-429C-45D4-9D28-D3E64DDB65E3}']
function Soma(X, Y : Double): Double; stdcall;
function Dife(X, Y : Double): Double; stdcall;
function Mult(X, Y : Double): Double; stdcall;
function Divi(X, Y : Double): Double; stdcall;
end;
implementation
uses InvokeRegistry;
initialization
InvRegistry.RegisterInterface(TypeInfo(IMath));
end.
Nessa unit encontramos uma declaração de interface normal, e esta será usada pelos
clientes para solicitar serviços junto ao servidor. Uma importante observação a ser
feita sobre a unit acima é o código localizado na seção initialization:
InvRegistry.RegisterInterface(TypeInfo(IMath));
Pronto, agora que já temos a definição do serviço que desejamos prover vamos agora
implementá-la.
Página 3 de 14
Desvendando Web Services (Soap/XML)
Quando vamos codificar as interfaces de nossos Web Services devemos criar classes
descendentes da TInvokable. Mas por que isso? São basicamente três os motivos que
nos levam a seguir esta convenção:
1 - Embora na implementação atual não tenha nada que realmente empeça que
derivemos nossa classes de uma outra qualquer como TObject ou TInterfacedObject,
assim como no caso da interface IUnknow/IInvokable, pode ser que em futuras
distribuições sejam criados métodos essenciais para o funcionamento da arquitetura na
Tinvokable, o que levaria nosso código a ter sérios problemas de compatibilidade e
funcionamento.
Mas se depois de tudo isso ainda quisermos usar outra ancestral para nossas classes
de implementação nós podemos. Vejamos abaixo o código da unit de implementação.
unit IMathImpl;
interface
type
//TMath = class(TInterfacedObject, IMath)
TMath = class(TInvokableclass, IMath)
public
function Soma(X: Double; Y: Double): Double; stdcall;
function Dife(X: Double; Y: Double): Double; stdcall;
function Mult(X: Double; Y: Double): Double; stdcall;
function Divi(X: Double; Y: Double): Double; stdcall;
end;
implementation
{ TMath }
Página 4 de 14
Desvendando Web Services (Soap/XML)
Result := X - Y;
end;
Result := TMath.Create;
end;
}
initialization
InvRegistry.RegisterInvokableClass(TMath);
//InvRegistry.RegisterInvokableClass(TMath, CreateMath);
end.
Repare nas linhas de código comentadas, visto que elas representam o básico
necessário para o funcionamento da arquitetura, se optarmos por não ter a Tinvokable
como ancestral de nossas classes. Ressalto que essa conduta deve ser evitada.
Novamente percebemos a necessidade de um código de registro, dessa vez o da
classe, que é feito como descrito abaixo.
InvRegistry.RegisterInvokableClass(TypeInfo(IMath));
Feito tudo isso até aqui, já podemos considerar que temos um servidor com suporte a
Web Services completo e funcional. Já até poderíamos passar para a implementação
de um cliente para usá-lo, mas ao invés disso vamos continuar a incrementá-lo com
mais alguns conceitos interessantes antes de passarmos ao desenvolvimento do
cliente.
Até agora só usamos um tipo de dado em nossa interface, o double. Embora essa
escolha tenha se dado devido ao serviço escolhido para o exemplo, o trabalho com
todos os outros tipos básicos(primitivos) funcionam da mesma maneira. Mas e se nós
precisarmos retornar para o cliente um tipo complexo como uma classe? Isso é o que
vamos ver a seguir.
Página 5 de 14
Desvendando Web Services (Soap/XML)
Sempre que precisarmos retornar ou receber tipos complexos tais como records, sets
ou classes em nossos Web Services, devemos mapeá-los para classes descendentes de
TRemotable. Na compilação destas, são incluídas informações de RTTI, usadas para
converter os dados em SOAP stream.
Então para exemplificarmos o uso desse recurso vamos definir uma outra interface
para o nosso Web Service, que usará e retornará um tipo complexo por nós definido.
Vamos analisar o código abaixo:
unit IGeometryIntf;
interface
uses InvokeRegistry;
type
TLosango = class(TRemotable)
private
FX1, FX2, FY1, FY2 : Integer;
published
property X1 : Integer read FX1 write FX1;
property X2 : Integer read FX2 write FX2;
property Y1 : Integer read FY1 write FY1;
property Y2 : Integer read FY2 write FY2;
end;
TPoint = class(TRemotable)
private
FX, FY : Integer;
published
property X : Integer read FX write FX;
property Y : Integer read FY write FY;
end;
IGeometry = interface(IInvokable)
['{926590CF-4B48-4AB2-9079-24183DD8D34F}']
function DiagonalMaior(const Los : TLosango) : Integer; stdcall;
function Losango(const Centro : TPoint; DiaU, DiaL : Integer) : TLosango; stdcall;
end;
implementation
initialization
InvRegistry.RegisterInterface(TypeInfo(IGeometry));
RemTypeRegistry.RegisterXSClass(TPoint);
RemTypeRegistry.RegisterXSClass(TLosango);
end.
Declaramos duas remotables classes TLosango e TPoint que como seus nomes
Página 6 de 14
Desvendando Web Services (Soap/XML)
Devemos observar na seção initialization dessa unit uma diferença entre o registro de
uma interface e de uma remotable classe.
initialization
InvRegistry.RegisterInterface(TypeInfo(IGeometry));
RemTypeRegistry.RegisterXSClass(TPoint);
RemTypeRegistry.RegisterXSClass(TLosango);
end.
Como podemos observar até aqui, no caso das interfaces e invokables classes nós
usamos os métodos RegisterInterface e RegisterInvokableClass do objeto InvRegistry,
e no caso das remotables classes e exceções personalizadas usamos o método
RegisterXSClass do objeto RemTypeRegistry, todos eles definidos na unit
InvokeRegistry.
unit IGeometryImpl;
interface
type
TGeometry = class(TInvokableClass, IGeometry)
public
function DiagonalMaior(const Los: TLosango): Integer; stdcall;
function Losango(const Centro: TPoint; DiaU: Integer; DiaL: Integer): TLosango;
stdcall;
end;
implementation
{ TGeometry }
Página 7 de 14
Desvendando Web Services (Soap/XML)
initialization
InvRegistry.RegisterInvokableClass(TGeometry);
end.
Bom, antes de passarmos a implementação do cliente vamos dar uma rápida olhada
no manuseio de exceções.
Todo programa bem implementado deve ter uma boa estrutura de exceções para
poder assegurar uma robustez desejável. No caso dos Web Services isso não deve ser
diferente.
Registrar as classes de exceção? Isso mesmo, como já podemos perceber tudo com
que trabalhamos em Web Services deve ser registrado. O registro das classes de
exceção se dá de forma idêntica a das descendentes de TRemotable que foi vista
anteriormente.
Página 8 de 14
Desvendando Web Services (Soap/XML)
Para exemplificar vamos criar agora uma exceção que será levantada quando o
método IGeometry.Losango receber no parâmetro Centro uma coordenada não
compreendida no primeiro quadrante. Então depois que definirmos a exceção vamos
ter de alterar esse método para implementar tal comportamento. Vejamos a seguir o
código de definição da exceção:
unit EInvalidCentroU;
interface
uses InvokeRegistry;
type
EInvalidCentro = class(ERemotableException)
private
FX, FY : Integer;
public
constructor Create(const X, Y : Integer);
published
property X : Integer read FX write FX;
property Y : Integer read FY write FY;
end;
implementation
{ EInvalidCentro }
initialization
RemTypeRegistry.RegisterXSClass(EInvalidCentro);
end.
Ufa! O caminho foi longo, mas chegamos lá. Com isso concluímos a codificação do
nosso servidor, vamos passar agora para a implementação do cliente.
Para iniciar vamos criar uma nova aplicação comum e deixá-la com a interface como
abaixo:
Página 9 de 14
Desvendando Web Services (Soap/XML)
Agora que já construímos a interface, vamos ver como requisitar os serviços remotos
do Web Services. Como já destaquei anteriormente, um cliente independe da
implementação do servidor, dessa forma, veremos como obter informações sobre um
servidor que já está rodando e disponível.
Todo servidor Web Services deve publicar informações sobre si mesmo no padrão
WSDL, no Delphi isso é feito automaticamente pela simples inclusão e configuração do
componente TWSDLHTMLPublish no projeto do servidor. Essas informações estão
acessíveis para nós, normalmente, através de uma action com path info "/wsdl". Nesse
caso estamos rodando o servidor localmente sobre o IIS5.0, então podemos acessar
essas informações solicitando a seguinte URL :
http://localhost/cgi-bin/WebServices.exe/wsdl
Página 10 de 14
Desvendando Web Services (Soap/XML)
Podemos encontrar então todas as interfaces implementadas pelo servidor bem como
um link para a descrição WSDL de cada interface. Para vermos essa descrição vamos
clicar no link de IMath. Fazendo isso seremos apresentados a seguinte tela:
OBS: Este comportamento acima descrito pode variar de acordo com a implementação
do servidor.
Página 11 de 14
Desvendando Web Services (Soap/XML)
Devemos repetir esse procedimento para cada interface que queremos importar, no
caso da IMath devemos preencher o campo acima com a seguinte URL:
http://localhost/cgi-bin/WebServices.exe/wsdl/IMath
Página 12 de 14
Desvendando Web Services (Soap/XML)
THTTPRIO
O componente THTTPRIO é o que usamos para obter uma referência válida de uma
interface registrada. Quando fazemos um type cast desse objeto para uma interface
específica ele gera uma tabela de métodos em memória que é usada quando é feita
uma chamada a um método específico.
Antes de começarmos a usar esse componente, devemos configurá-lo, isso pode ser
feito de duas maneiras diferentes:
Página 13 de 14
Desvendando Web Services (Soap/XML)
Im : IMath;
A, B : Double;
begin
Im := HRMath as IMath;
A := StrToFloat(EdtX.Text);
B := StrToFloat(EdtY.Text);
LblResult.Caption := FloatToStr(Im.Soma(A, B));
end;
Podemos perceber que o uso dele é bem simples. Basta declararmos uma variável do
tipo da interface desejada e atribuirmos a ela o type cast do componente. Depois de
feito isso, podemos usá-la normalmente como se fosse um objeto. Lembre-se também
que não é necessária a liberação de memória correspondente a referência da interface,
ela é liberada automaticamente quando a variável sai de escopo. Esse procedimento
deve ser repetido para todos os métodos que desejamos requisitar.
Para uma melhor compreensão do assunto abordado, recomendo que seja analisado os
fontes dos projetos criados ao longo deste artigo. Fontes disponíveis em xxxxxxxxxx.
TSoapConnection
O componente TSoapConnection pode ser usado por uma aplicação cliente que deseja
conectar-se a uma mult-tiered database application, implementada como um Web
Services. Ele nos dá a possibilidade de estabelecer a conexão entre servidor e cliente e
obter a IAppServer do servidor, implementado num RemoteDataModule, a partir da
qual temos acesso aos providers existentes nele.
Esse componente traz algumas vantagens em relação aos outros existentes que
desempenham funções parecidas. Dentre essas vantagens, destacamos o uso do HTTP
para transporte das informações, bem como o suporte a SSL. Podemos ter segurança
na transmissão de dados e ainda evitamos problemas com proxys.
Consulte o help online para informações sobre pré-requisitos para o uso deste
componente. O componente TSoapConnection desempenha mais ou menos o mesmo
papel dos componentes TDCOMConnection, TSocketConnection, TWebConnection,
TCORBAConnection.
Conclusão
Termino este artigo desejando que as todas as expectativas daqueles que iniciaram a
sua leitura tenham sido satisfeitas. Agradecerei a todos que venham a opiná-lo ou
comentá-lo.
Página 14 de 14