Atenção: esse artigo tem uma palestra complementar. Clique e assista!

Artigo no estilo: Curso

Do que trata o artigo

Desenvolvimento de aplicações clientes para consumir Web Services. Essas aplicações serão desenvolvidas utilizando diferentes linguagens de programação e consumirão um mesmo Web Service desenvolvido na edição 107.


Para que serve

Permitir que nós possamos oferecer a mesma solução para um cliente em diferentes formatos. Para empresas que optam por solução free oferecemos PHP rodando em Linux. Para uma empresa com infra Microsoft, como IIS, oferecemos uma solução em ASP.NET e para pequenas empresas podemos oferecer uma aplicação Win32.

Em que situação o tema é útil

Num cenário onde temos que integrar diferentes tecnologias, como servidores escritos em PHP e clientes em ASP.NET. Hoje é muito comum as empresa portarem seus sistemas e serviços para Web. Com o uso de Web Services podemos criar diversos clientes para consumir o mesmo serviço de forma transparente.

Resumo do DevMan

Na edição 107 mostramos como criar Web Services com Delphi for PHP. Criamos um pequeno Web Service com quatro métodos que acessam um banco de dados MySql. Neste artigo criaremos três aplicações clientes para consumir este Web Service. Assim a leitura do artigo da edição 107 se faz obrigatória para um perfeito entendimento do assunto, visto que toda a parte teórica foi abordada no artigo anterior.

Na edição 107 eu abordei o que são e como criar Web Services no Delphi for PHP. Terminamos o artigo com o Web Service pronto, porém não vimos como consumi-lo, isso será feito neste artigo. Eu não abordarei os detalhes técnicos e de funcionamento de um Web Service, pois isso já foi feito no artigo anterior. Assim vamos ficar apenas com a parte prática. Criaremos três aplicações, uma em cada tecnologia, para ver na prática as vantagens de se oferecer serviços através de Web Services.

Publicando o serviço

Antes de começarmos a desenvolver as aplicações para consumir nosso Web Service, publicarei o Web Service que desenvolvemos na edição passada em um provedor próprio que tenho com suporte à PHP. Assim ficará mais fácil o desenvolvimento dos exemplos, sem falar que você enquanto lê e desenvolve estes exemplos poderá consumir um serviço real hospedado na Web.

A primeira coisa que vou fazer é abrir o projeto do Web Service no Delphi for PHP. Feito isso vou trocar todas as referências que faço ao banco de dados local, ou seja, no meu código em todos os lugares em que conecto ao servidor localhost irei alterar para o endereço de um banco de dados que tenho hospedado no meu servidor Web. Meu servidor MySql fica em mysql.rmfactory.com.br. Por questões de segurança não irei informar aqui o usuário e senha para acesso à base de dados.

Feitas as alterações é hora de gerar os scripts para enviar ao servidor. Para isso eu acessei o menu Tools>Deployment Wizard. Essa ferramenta irá coletar todos os scripts do seu projeto e da VCL que seu projeto utiliza e colocará num diretório que você especificar. Observe o Wizard em operação na Figura 1.

Figura 1. Deployment Wizard

Ao final, dentro do diretório especificado, estarão os seus scripts e uma pasta VCL. Nesta pasta estão os scripts necessários para o funcionamento da aplicação. Basta enviar o conteúdo do diretório para seu servidor Web. Eu o fiz e o Web Service está hospedado em http://www.rmfactory.com.br/services/devmedia.

Com o serviço já hospedado e rodando basta apenas consumi-lo. Se algum dia, alguém chegar para você e lhe disser: "Teremos que construir um cliente para um Web Service", então a primeira coisa que você irá precisar é o WSDL. O Delphi Win32 e Delphi .NET já convertem o WSDL em um arquivo .pas com todas as classes necessárias para consumir os serviços. WSDL é a sigla de Web Services Description Language, padrão baseado em XML. Funciona como uma espécie de “TypeLibrary” do Web Service, além de ser usado para a validação das chamadas dos métodos (veja mais informações na Nota do DevMan). O endereço WSDL do nosso WebService está em http://www.rmfactory.com.br/services/devmedia/WSDevMedia.php?wsdl.

Nota do DevMan

WSDL (WebService Description Language) é uma especificação desenvolvida pelo W3C que permite descrever Web Services segundo um formato XML, independentemente dos formatos de mensagem e dos protocolos de rede que sejam usados. A WSDL descreve os serviços disponibilizados à rede através de uma semântica XML, de forma que toda a informação necessária para se chamar um sistema distribuído e o procedimento necessário para que esta comunicação se estabeleça já estejam também descritos. Enquanto o SOAP especifica a comunicação entre um cliente e um servidor (o protocolo), a WSDL descreve os serviços oferecidos.

Criando o cliente Win32

Abra o Delphi e crie uma nova aplicação VCL. Salve a aplicação em um diretório de sua preferência. Renomeie a unit1 para uFrmPrincipal e o projeto para ClienteWin32. A primeira coisa que vamos fazer é importar a WSDL. O Delphi traz um utilitário chamado WSDL Importer que faz o mapeamento da WSDL e gera uma unit com os métodos prontos para consumir os serviços, o que chamamos de classe proxy. Acesse o menu File>New>Other e no diálogo que se abre selecione Delphi Projects>WebServices e dê um duplo clique no item WSDL Importer (Figura 2). Informe o endereço do descritor de serviço, no nosso caso o endereço da WSDL que indiquei anteriormente (Figura 3).

Figura 2. Acessando o WSDL Importer

Figura 3. Importando a WSDL

Ao pressionar Next o utilitário mostrará um resumo dos métodos que estão descritos na WSDL e que serão importados para a unit. Pressione Finish. Uma unit será gerada e nela temos o código descrito na Listagem 1.

Listagem 1. Código gerado pelo WSDL Importer


  Web_Service_WorldPortType = interface(IInvokable)
    ['{677D3F52-BE48-FE65-7525-F166048CC560}']
      function  HelloWorld(const mesagem: WIDEString): WIDEString; stdcall;
      function  CountryByContinent(const Continent: WIDEString): WIDEString; stdcall;
      function  LanguagesByCountry(const Country: WIDEString): WIDEString; stdcall;
      function  GetSimpleCountry(const Country: WIDEString): WIDEString; stdcall;
    end;
   
  function GetWeb_Service_WorldPortType(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): Web_Service_WorldPortType; 
...

No código da Listagem 1 foram omitidos os comentários que por sinal possuem algumas informações úteis como namespaces, url etc. O que o utilitário fez na verdade foi criar uma interface com os métodos contidos em nosso Web Service. Entenda esta interface como um “controle remoto”, a forma que usamos para efetuar a comunicação com o servidor. Uma função também é criada e tem por objetivo criar um objeto HTTPRio, que será responsável por conectar ao Web Service e invocar o método especificado.

Nota do DevMan

Em algumas linguagens de programação, o termo interface é uma referência à característica que permite a construção de aplicações que isolam do mundo exterior os detalhes de implementação de um componente de software. Um exemplo clássico de utilização de interfaces é o do sistema operacional que, através de uma interface de programação de aplicativos (API), permite que os programas utilizem os recursos do sistema (memória, CPU etc.) sem que os seus detalhes de implementação sejam conhecidos do programador. Este esquema isola e protege o sistema operacional de eventuais erros cometidos pela aplicação.

Desta forma o acesso ao servidor fica transparente. Basta utilizar a função GetWeb_Service_WorldPortType que retorna um ponteiro para a interface Web_Service_WorldPortType. Com isso temos acesso aos métodos e podemos consumir os serviços. Vamos aqui consumir apenas um método, pois a maneira será a mesma para acessar os demais. Vamos utilizar o método GetSimpleCountry como exemplo. Vale lembrar que este método retorna as informações de um determinado país, informado no parâmetro Country. O retorno da função é uma String, mas não esqueça que esta string é o conteúdo de um arquivo XML. Então como exibir os dados que serão retornados no XML? Podemos mapeá-lo através do componente XMLDocument ou então utilizar o já conhecido ClientDataSet.

Nota do DevMan

TXMLDocument é um componente disponível na VCL do Delphi que implementa as interfaces do modelo DOM para manipulação de arquivos XML. Com ele é possível tanto ler como gerar arquivos XML.

O problema é que o XML utilizado pelo ClientDataSet é do tipo DataPacket, um tipo específico usado pelo DataSnap. O nosso XML não possui esse formato, então para que possamos exibir os dados em um DBGrid, por exemplo, teremos que transformá-lo em um DataPacket para que o mesmo possa ser entendido por um ClientDataSet. Aqui entra em cena o XML Mapper.

Nota do DevMan

O DataPacket é uma estrutura utilizada internamente pelo ClientDataSet. Está estrutura é organizada em formato XML e é este DataPacket que é trafegado entre o Cliente e o Servidor de aplicação em projetos desenvolvidos em camadas.

A função do XML Mapper é gerar um arquivo de transformação para que possamos converter um arquivo XML em um DataPacket e vice e versa. Não entrarei em detalhes sobre o uso do XML Mapper por não ser escopo deste artigo.

Nota do Editor: a edição 36 apresenta um artigo completo sobre XML Mapper, intercâmbio de dados em XML e aplicações B2B (Business to Business).

Para que possamos gerar o arquivo de transformação temos que ter em mãos o arquivo XML que queremos converter. Então somente em caráter excepcional vamos consumir um método do Web Service apenas para gerar um arquivo XML. Insira,um botão no form e no evento onClick digite o código da Listagem 2.

Listagem 2. Gerando o arquivo XML


  1 var
  2  S: TStringList;
  3 begin
  4  S := TStringList.Create();
  5  S.Add(GetWeb_Service_WorldPortType.GetSimpleCountry ('Brazil'));
  6  S.SaveToFile('country.XML');
  7  FreeAndNil(S);
  8 end; 

O código é bem simples. Nós temos uma variável do tipo TStringList na linha 2. Este objeto será usado apenas para que possamos usar o método SaveToFile (linha 6) e gerar o arquivo. Na linha 5 invocamos o método GetSimpleCountry passando um país. Este método retorna um conteúdo do tipo WideString que na verdade é o conteúdo do nosso XML. Nós apenas colocamos este conteúdo dentro do objeto TStringList e o salvamos como country.xml. Dentro do IDE acesse o menu Tools>XML Mapper. Será aberto o utilitário mostrado na Figura 4.

Figura 4. XML Mapper

Na tela principal do XML Mapper acesse o menu File>Open e abra o arquivo country.xml que acabamos de gerar. Ao executar este procedimento, o XML Mapper exibirá os nós que estão contidos no XML. Clique com o botão direito sobre qualquer um dos nós e selecione a opção Select All. Isso fará com que todos os nós sejam mapeados e fiquem prontos para serem transformados. Ao realizar este procedimento, o XML Mapper deverá estar como a Figura 5.

Figura 5. Arquivo XML carregado

Feito isso vamos gerar a transformação. Para isso acesse o menu Create>DataPacket From XML. Ao realizar este procedimento, o XML Mapper mostra do lado direito a estrutura do DataPacket (Figura 6).

Figura 6. DataPacket gerado

Para finalizar o processo vamos salvar um arquivo .xtr para que possamos realizar esta transformação em tempo de execução. Acesse o menu Create>Transformation e a seguir File>Save> Transformation. Salve o arquivo ToDp.xtr no mesmo diretório da aplicação.

No formulário principal da aplicação vamos adicionar os seguintes componentes: ClientDataSet, DataSource, DBGrid, Edit, Button e XMLTransform. Conecte o DBGrid ao DataSource e o DataSource ao ClientDataSet. Posicione o botão ao lado do Edit e os dois acima do DBGrid. A Figura 7 mostra um exemplo de como deverá estar seu formulário.

Figura 7. Formulário principal

A função do componente XMLTransform é exatamente transformar o XML que virá do nosso Web Service para o formato DataPacket, para que este possa ser lido e carregado no ClientDataSet. Vá ao evento OnClick do botão e coloque o código da Listagem 3.

Listagem 3. Transformando o XML com XMLTransform


  var
    S: String;
  begin
    S := GetWeb_Service_WorldPortType.GetSimpleCountry(Edit1.Text);
    ClientDataSet1.XMLData := XMLTransform1.TransformXML(S,'todp.xtr');
  end; 

Primeiro invocamos o método GetSimpleCountry e passamos para o mesmo o nome do país informado no Edit1. O retorno desta função é guardado numa variável chamada S. Lembre-se que o conteúdo desta variável é na verdade um arquivo XML retornado pelo nosso Web Service. Feito isso nós utilizamos o componente XMLTransform e chamamos o método TransformXML, passando para ele a variável S e o nome do arquivo .xtr que criamos com o XML Mapper. Este método retornará um DataPacket que nós atribuiremos à propriedade XMLData do ClientDataSet. Rode a aplicação, digite o país Brazil, por exemplo, e observe o resultado (Figura 8).

Figura 8. Aplicação em execução

Criando o cliente ASP.NET

No RAD Studio crie uma nova aplicação ASP.NET. No diálogo que se abrir informe o nome do projeto como ClienteAspNet. Seu projeto já traz uma página Web e nela vamos adicionar um componente TextBox, um botão e um componente GridView. Disponha os componentes conforme a Figura 9.

Figura 9. Interface da aplicação ASP.NET

Interface criada vamos agora importar o WSDL para nossa aplicação. Para isso vá até o Project Manager e clique com o botão direto sobre o item ClientAspNet.dll. No menu que vai se abrir clique em Add Web Reference. Isso abrirá um utilitário que é mostrado na Figura 10.

Figura 10. Importando a WSDL em .NET

Informe o endereço do WSDL que você quer importar, no nosso caso o mesmo utilizado na aplicação Win32. Clique no botão com a figura de uma seta azul para que seja gerado o código para consumirmos nosso Web Service. Em Web Reference Folder Name digite WSDLRef e clique no botão Add Reference. Repare que no Project Manager foi adicionada uma nova unit. A Figura 11 exibe a nova estrutura.

Figura 11. Web References

O arquivo gerado em .NET é relativamente maior do que o gerado em Win32. Isto porque em .NET temos a opção, entre outras coisas, de fazer chamadas assíncronas ao métodos do nosso servidor Web. Por isso, para cada método no Web Service, temos três ou mais funções para consumi-lo.

Dê um duplo clique no botão que está na nossa página ASP.NET e no evento Click insira o código da Listagem 4.

Listagem 4. Consumindo o serviço no .NET


  1.   var
  2.    WS: WebServiceWorld;
  3.    Stream: StringReader;
  4.    XmlReader : XmlTextReader;
  5.    DS: DataSet;
  6.   begin
  7.    Ws := WebServiceWorld.Create();
  8.    Stream := StringReader.Create(WS.CountryByContinent(TextBox1.Text));
  9.    XmlReader := XmlTextReader.Create(Stream);
  10.   Ds := DataSet.Create();
  11.   Ds.ReadXml(XmlREader);
  12.   GridView1.DataSource := DS;
  13.   GridView1.DataBind();
  14.  end; 

Repare que em vista do código que escrevemos em Win32 este código em .NET está com algumas linhas a mais. Porém não houve em .NET a necessidade de se criar um arquivo de transformação. Isto porque a maioria dos objetos em .NET já são 100% compatíveis com fontes de dados em XML.

Começamos com um objeto do tipo WebServiceWorld que instanciamos na linha 7. Estes objetos (proxy) possuem todos os métodos que estão disponíveis em nosso servidor web. Na linha 8 nós criamos um objeto do tipo StringReader. Ele irá fazer um stream, ou seja, colocará na memória o retorno da função que iremos chamar no Web Service, neste caso o retorno da função CountryByContinent. Então ao clicarmos no botão, iremos invocar o método CountryByContinent através do objeto WebServiceWorld e o seu retorno passamos para o objeto StringReader. Na linha 9 instanciamos um objeto XmlTextReader responsável por ler documentos XML. No nosso caso estamos lendo o conteúdo do nosso stream e já o preparando para fazer bind no GridView (ou seja, exibir os dados). Na linha 10 criamos um objeto DataSet, que mantêm uma estrutura de dados em memória. Ao criarmos o DataSet chamamos o método ReadXML para carregar o conteúdo XML que está na variável XmlReader. Dados carregados apenas passamos o DataSet para a propriedade DataSource do GridView e os dados já serão exibidos (Figura 12).

Figura 12. Aplicação ASP.NET em execução

Criando o cliente PHP

Chegamos à última das aplicações clientes que vamos criar neste artigo e para finalizar iremos desenvolver uma pequena aplicação com Delphi for PHP para consumir um método do nosso Web Service. Então vamos abrir o Delphi for PHP e através do menu File>New>Application criar uma nova aplicação. Salve-a e no formulário adicione um botão e dois Edits. Aqui vamos invocar o método HelloWorld presente em nosso Web Service.

Para consumir um Web Service em PHP temos que fazer uso de uma classe chamada nusoap. Ela possui os métodos necessários para essa integração. Assim a primeira coisa que faremos é incluir uma referência ao script nusoap.php. Para isso adicione a seguinte linha na seção uses do formulário: require_once('vcl/nusoap/nusoap.php'). Agora no evento onClick do botão insira o código da Listagem 5.

Listagem 5. Consumindo o Web Service em PHP


  $wsdl = "http://www.rmfactory.com.br/services/devmedia/WSDevMedia.php?wsdl";
  $client = new nusoapclient($wsdl, true);
  $parameters = array('mesagem' => $this->Edit1->Text);
  $this->Edit2->Text = $client->call('HelloWorld', $parameters); 

No código da Listagem 5 criamos um objeto nusoapclient passando para ele o endereço da WSDL. Feito isso criamos um array para passar o parâmetro solicitado pela função HelloWorld. Na sequência utilizamos o método Call para invocar o método HelloWorld e passamos o array com o parâmetro. O retorno da função será a string passada formatada com a primeira letra de cada palavra em maiúscula. A Figura 13 mostra a aplicação em execução.

Figura 13. Aplicação cliente em PHP

Conclusão

Seja em Win32, PHP ou ASP.NET ficou claro que desenvolver aplicações integradas a Web Service é algo muito interessante, útil e necessário, tanto do ponto de vista técnico quanto financeiro. O assunto abordado neste e no anterior orientou você a oferecer serviços web e integrar suas aplicação com as dos seus clientes e fornecedores.