Introdução
Quem não conhece, já ouviu falar de Web Services. Traduzindo em pouquíssimas palavras, Web Services é um padrão de comunicação entre sistemas, onde o provedor ( ou “servidor” ) de um Web Service, possui uma camada de publicação de classes e métodos, descritos em um documento baseado em XML, chamado de WSDL ( Web Services Description Language ), através do qual uma linguagem ou plataforma de desenvolvimento pode criar uma classe ou camada de “proxy” para acessar as funcionalidades descritas no WSDL. Normalmente os Web Services utilizam o protocolo HTTP para trocas de mensagens entre a aplicação cliente e o servidor, e são utilizados entre sistemas que podem ou não ser baseados na mesma plataforma ou tecnologia, e as aplicações client e server podem estar na mesma rede ( intranet ) ou na Internet.
WSDL
A especificação da descrição de um Web Service é bem ampla, ela prevê a descrição detalhada das classes e métodos disponíveis, descrição das estruturas de parâmetros e retornos, até mesmo a documentação textual de cada método pode ser especificada dentro do WSDL. Normalmente uma plataforma de desenvolvimento que trabalha com orientação a objetos e é compatível nativamente com Web Services, provê um tipo de classe diferenciada para permitir a publicação de uma classe implementada no formato nativo da linguagem para ser acessada como um Web Service.
SOAP
Soap é um acrônimo para Simple Object Access Protocol, trata-se de uma especificação para troca de informações estruturadas, baseado em XML. O WSDL fornecido descreve como devem ser os pacotes SOAP de envio e retorno de informações de um Web Service. A especificação das mensagens normalmente são simples, porém a especificação permite criar camadas complexas, chegando até mesmo ao nível de declaração de mensagens com herança e utilização de mais de um arquivo de definições, especificados como “includes” ou “imports” dentro dos WSDL(s).
Vantagens
Quando você usa uma plataforma ou ambiente de desenvolvimento que cria uma classe “proxy” para você consumir um Web Service, o mundo é maravilhoso, o céu é azul, tudo é lindo. Você não precisa saber ou entender o que vai dentro do XML, a classe monta tudo para você, basta alimentar os dados nas estruturas de parâmetro e consumir o serviço, e em caso de sucesso a(s) estrutura(s) de retorno estarão preenchidas. Com o detalhamento do WSDL, a classe proxy gerada pode até já vir documentada. A utilização do protocolo HTTP para envio de requisições torna mais fácil a criação de integrações de sistemas baseados em SOA (Service-Oriented Architecture), ou Arquitetura Orientada a Serviços.
Desvantagens
Quando falamos de transações curtas, requisições pequenas, Web Services é ótimo. Mas se você vai enfiar em uma única requisição um caminhão de dados, ou mesmo arquivos, a natureza de parser de XML — que normalmente exige que o documento inteiro seja parseado ou analisado antes do método chamado ser efetivamente executado — ou mesmo qualquer instabilidade no HTTP, ou picos de latência, podem tornar o processamento mais lento, devido ao overhead das camadas utilizadas, e com isso deixar o cliente do serviço esperando por muito tempo, o que não é uma boa prática. Como toda a solução tecnológica, seu uso deve ser mediante a necessidade, quando aplicável ao cenário proposto. Se você precisa transferir arquivos grandes para um processamento, faça-o via FTP, e utilize um WebService apenas para solicitar a requisição de processamento, informando o arquivo a ser utilizado. Lembrando que esta é uma prerrogativa que o desenvolvedor da parte “Server” do Web Service deve ser responsável.
Web Services no AdvPL
Desde 2002, o Protheus permite a criação de Web Services Server no repositório, bastando para isso escrever uma classe do tipo WSSERVER, e um FrameWork da LIB do ERP Microsiga provê a camada WEB de publicação ( WSDL ) e consumo. E, para a geração de uma classe proxy de um WebService, o IDE e o TDS possuem uma ferramenta de geração de código, que gera a classe client AdvPL ( WSCLIENT ), também utilizando um FrameWork da LIB do ERP Microsiga, para consumir o serviço.
A implementação dos Web Services em AdvPL (client-side) não consegue atender a especificação inteira de todas as versões de SOAP, mas atende a maioria das partes mais utilizadas das especificações. Existem algumas dificuldades com o uso de WebServices onde o WSDL “mescla” namespaces distintos nos serviços, e às vezes alguma propriedade específica da especificação é utilizada, e exige um pacote diferenciado. Uma vez que a classe proxy seja gerada em AdvPL, é possível customizá-la para atender estas necessidades, mas o desenvolvedor deve lembrar-se de que: Se ele precisar re-gerar a classe proxy, as alterações feitas por ele no código serão perdidas, portanto quando isso for mesmo necessário, cabe ao desenvolvedor manter um histórico do que ele precisou mexer na classe client, para ele refazer as mesmas alterações caso a classe client venha a ser re-gerada.
No TDN, existe uma árvore de tópicos sobre Web Services, dispinível a partir do Link http://tdn.totvs.com/display/tec/Web+Services+–+23597, onde inclusive é abordado um outro tipo de Web Service, chamado REST, que não usa XML para a troca de dados entre a aplicação cliente e servidora. Este post não substitui a documentação da TDN, apenas procura colocar alguns detalhes a mais, em uma linguagem um pouco mais informal, sobre a tecnologia envolvida.
Nova classe Client no AdvPL
Nas builds do TOTVS|Application Server, a partir da 7.00.121227P, foi publicada uma nova classe client de Web Services na linguagem AdvPL, chamada “tWSDLManager“. Ela é uma classe proxy dinâmica, onde você fornece para ela o WSDL do serviço, ela identifica na hora todas as classes e métodos disponíveis para serem consumidas, e provê métodos para que você consuma o serviço diretamente pela classe, ou utilize a classe como “core” de WebSErvices, para montar o pacote com a requisição, e o desenvolvedor poder utilizar outro meio de transporte além do HTTP para a transmissão dos dados.
Ela não é tão prática quanto a implementação anterior, onde uma classe proxy em AdvPL era gerada e compilada no repositório de objetos do ERP, porém ela foi construída com um núcleo mais robusto, e compatível com elementos não suportados pela classe proxy WSCLIENT. Então, este tópico vamos ver como usar esta classe para, por exemplo, consumir um Web Service publico na Internet.
Usando um Web Service na Internet
Vamos usar um serviço de conversão de moedas, disponível no endereço http://www.webservicex.net/CurrencyConvertor.asmx. Ao acessar esta página,
vemos que não se trata de um Web Site tradicional, mas uma página de publicação com detalhes do serviço. O provedor deste serviço utilizou o ASP .NET para fazê-lo. O que nos interessa para consumir o serviço é o WSDL, disponivel no link da página onde é mostrado em destaque “Service Description”. Clique neste link, e o WSDL será mostrado no seu Browser. Agora, pegue a URL mostrada no Browse ( http://www.webservicex.net/CurrencyConvertor.asmx?WSDL ) e copie para a área de transferência do Windows ( o famoso Control+C ).
Gerando a classe Client em AdvPL
Pelo método tradicional, usando o TDS, vamos gerar a classe client em AdvPL para consumir este serviço. Antes de mais nada, após abrir o TDS, entre na perspectiva de “Administrador TOTVS”, e na aba “Servidor”, configure ou selecione um servidor do TOTVS Application Server previamente configurado e conecte nele. Sem a conexão com um Application Server, a geração de código AdvPL para WebServices não funciona.
Utilizando o TDS 11.3, na perspectiva “Desenvolvedor Totvs”, localize a janela “Explorador de Projetos”, normalmente do lado esquerdo do vídeo, escolha ou crie uma pasta no projeto para acomodar o fonte gerado, e clicando com o botão direito sobre a pasta, escolha a opção Novo -> Outras, e na tela apresentada, escolha Advpl -> Novo fonte WSDL AdvPL.
Clique em “avançar”, e será mostrado uma tela perguntando os dados pertinentes do serviço. Na primeira informação (local), é mostrada a pasta do projeto onde o fonte será criado. Pode ser mantida a informação lá existente. Na segunda informação (nome do fonte), devemos informar o nome do fonte AdvPL que será criado com a classe client. No nosso exemplo, informe “ConvMoedas”, e no campo “url”, cole a URL do WSDL do serviço, anteriormente copiada para a área de transferência do Windows na etapa anteriormente descrita. Então, clique no botão “Concluir”.
Se não houver nenhum problema com a conexão com a Internet, o fonte “ConvMoedas.prw” deverá ser criado, e adicionado automaticamente ao Projeto aberto no TDS. PAra compilar o fonte, basta estar com o foco na janela do editor de código deste fonte, e pressionar as teclas Control+F9. Vamos ver o fonte gerado no meu ambiente de testes:
#INCLUDE "PROTHEUS.CH" #INCLUDE "APWEBSRV.CH"
/* =============================================================================== WSDL Location http://www.webservicex.net/CurrencyConvertor.asmx?WSDL Gerado em 03/27/15 21:22:35 Observações Código-Fonte gerado por ADVPL WSDL Client 1.120703 Alterações neste arquivo podem causar funcionamento incorreto e serão perdidas caso o código-fonte seja gerado novamente. =============================================================================== */
User Function _HKRQIVL ; Return // "dummy" function - Internal Use
/* ------------------------------------------------------------------------------- WSDL Service WSCurrencyConvertor ------------------------------------------------------------------------------- */
WSCLIENT WSCurrencyConvertor
WSMETHOD NEW WSMETHOD INIT WSMETHOD RESET WSMETHOD CLONE WSMETHOD ConversionRate
WSDATA _URL AS String WSDATA _HEADOUT AS Array of String WSDATA _COOKIES AS Array of String WSDATA oWSFromCurrency AS CurrencyConvertor_Currency WSDATA oWSToCurrency AS CurrencyConvertor_Currency WSDATA nConversionRateResult AS double
ENDWSCLIENT
WSMETHOD NEW WSCLIENT WSCurrencyConvertor ::Init() If !FindFunction("XMLCHILDEX") UserException("O Código-Fonte Client atual requer os executáveis do Protheus Build [7.00.131227A-20150220] ou superior. Atualize o Protheus ou gere o Código-Fonte novamente utilizando o Build atual.") EndIf If val(right(GetWSCVer(),8)) < 1.040504 UserException("O Código-Fonte Client atual requer a versão de Lib para WebServices igual ou superior a ADVPL WSDL Client 1.040504. Atualize o repositório ou gere o Código-Fonte novamente utilizando o repositório atual.") EndIf Return Self
WSMETHOD INIT WSCLIENT WSCurrencyConvertor ::oWSFromCurrency := CurrencyConvertor_CURRENCY():New() ::oWSToCurrency := CurrencyConvertor_CURRENCY():New() Return
WSMETHOD RESET WSCLIENT WSCurrencyConvertor ::oWSFromCurrency := NIL ::oWSToCurrency := NIL ::nConversionRateResult := NIL ::Init() Return
WSMETHOD CLONE WSCLIENT WSCurrencyConvertor Local oClone := WSCurrencyConvertor():New() oClone:_URL := ::_URL oClone:oWSFromCurrency := IIF(::oWSFromCurrency = NIL , NIL ,::oWSFromCurrency:Clone() ) oClone:oWSToCurrency := IIF(::oWSToCurrency = NIL , NIL ,::oWSToCurrency:Clone() ) oClone:nConversionRateResult := ::nConversionRateResult Return oClone
// WSDL Method ConversionRate of Service WSCurrencyConvertor
WSMETHOD ConversionRate WSSEND oWSFromCurrency,oWSToCurrency WSRECEIVE nConversionRateResult WSCLIENT WSCurrencyConvertor Local cSoap := "" , oXmlRet
BEGIN WSMETHOD
cSoap += '' cSoap += WSSoapValue("FromCurrency", ::oWSFromCurrency, oWSFromCurrency , "Currency", .T. , .F., 0 , NIL, .F.) cSoap += WSSoapValue("ToCurrency", ::oWSToCurrency, oWSToCurrency , "Currency", .T. , .F., 0 , NIL, .F.) cSoap += ""
oXmlRet := SvcSoapCall( Self,cSoap,; "http://www.webserviceX.NET/ConversionRate",; "DOCUMENT","http://www.webserviceX.NET/",,,; "http://www.webservicex.net/CurrencyConvertor.asmx")
::Init() ::nConversionRateResult := WSAdvValue( oXmlRet,"_CONVERSIONRATERESPONSE:_CONVERSIONRATERESULT:TEXT","double",NIL,NIL,NIL,NIL,NIL,NIL)
END WSMETHOD
oXmlRet := NIL Return .T.
// WSDL Data Enumeration Currency
WSSTRUCT CurrencyConvertor_Currency WSDATA Value AS string WSDATA cValueType AS string WSDATA aValueList AS Array Of string WSMETHOD NEW WSMETHOD CLONE WSMETHOD SOAPSEND WSMETHOD SOAPRECV ENDWSSTRUCT
WSMETHOD NEW WSCLIENT CurrencyConvertor_Currency ::Value := NIL ::cValueType := "string" ::aValueList := {} aadd(::aValueList , "AFA" ) aadd(::aValueList , "ALL" ) aadd(::aValueList , "DZD" ) aadd(::aValueList , "ARS" ) aadd(::aValueList , "AWG" ) aadd(::aValueList , "AUD" ) aadd(::aValueList , "BSD" ) aadd(::aValueList , "BHD" ) aadd(::aValueList , "BDT" ) aadd(::aValueList , "BBD" ) aadd(::aValueList , "BZD" ) aadd(::aValueList , "BMD" ) aadd(::aValueList , "BTN" ) aadd(::aValueList , "BOB" ) aadd(::aValueList , "BWP" ) aadd(::aValueList , "BRL" ) aadd(::aValueList , "GBP" ) aadd(::aValueList , "BND" ) aadd(::aValueList , "BIF" ) aadd(::aValueList , "XOF" ) aadd(::aValueList , "XAF" ) aadd(::aValueList , "KHR" ) aadd(::aValueList , "CAD" ) aadd(::aValueList , "CVE" ) aadd(::aValueList , "KYD" ) aadd(::aValueList , "CLP" ) aadd(::aValueList , "CNY" ) aadd(::aValueList , "COP" ) aadd(::aValueList , "KMF" ) aadd(::aValueList , "CRC" ) aadd(::aValueList , "HRK" ) aadd(::aValueList , "CUP" ) aadd(::aValueList , "CYP" ) aadd(::aValueList , "CZK" ) aadd(::aValueList , "DKK" ) aadd(::aValueList , "DJF" ) aadd(::aValueList , "DOP" ) aadd(::aValueList , "XCD" ) aadd(::aValueList , "EGP" ) aadd(::aValueList , "SVC" ) aadd(::aValueList , "EEK" ) aadd(::aValueList , "ETB" ) aadd(::aValueList , "EUR" ) aadd(::aValueList , "FKP" ) aadd(::aValueList , "GMD" ) aadd(::aValueList , "GHC" ) aadd(::aValueList , "GIP" ) aadd(::aValueList , "XAU" ) aadd(::aValueList , "GTQ" ) aadd(::aValueList , "GNF" ) aadd(::aValueList , "GYD" ) aadd(::aValueList , "HTG" ) aadd(::aValueList , "HNL" ) aadd(::aValueList , "HKD" ) aadd(::aValueList , "HUF" ) aadd(::aValueList , "ISK" ) aadd(::aValueList , "INR" ) aadd(::aValueList , "IDR" ) aadd(::aValueList , "IQD" ) aadd(::aValueList , "ILS" ) aadd(::aValueList , "JMD" ) aadd(::aValueList , "JPY" ) aadd(::aValueList , "JOD" ) aadd(::aValueList , "KZT" ) aadd(::aValueList , "KES" ) aadd(::aValueList , "KRW" ) aadd(::aValueList , "KWD" ) aadd(::aValueList , "LAK" ) aadd(::aValueList , "LVL" ) aadd(::aValueList , "LBP" ) aadd(::aValueList , "LSL" ) aadd(::aValueList , "LRD" ) aadd(::aValueList , "LYD" ) aadd(::aValueList , "LTL" ) aadd(::aValueList , "MOP" ) aadd(::aValueList , "MKD" ) aadd(::aValueList , "MGF" ) aadd(::aValueList , "MWK" ) aadd(::aValueList , "MYR" ) aadd(::aValueList , "MVR" ) aadd(::aValueList , "MTL" ) aadd(::aValueList , "MRO" ) aadd(::aValueList , "MUR" ) aadd(::aValueList , "MXN" ) aadd(::aValueList , "MDL" ) aadd(::aValueList , "MNT" ) aadd(::aValueList , "MAD" ) aadd(::aValueList , "MZM" ) aadd(::aValueList , "MMK" ) aadd(::aValueList , "NAD" ) aadd(::aValueList , "NPR" ) aadd(::aValueList , "ANG" ) aadd(::aValueList , "NZD" ) aadd(::aValueList , "NIO" ) aadd(::aValueList , "NGN" ) aadd(::aValueList , "KPW" ) aadd(::aValueList , "NOK" ) aadd(::aValueList , "OMR" ) aadd(::aValueList , "XPF" ) aadd(::aValueList , "PKR" ) aadd(::aValueList , "XPD" ) aadd(::aValueList , "PAB" ) aadd(::aValueList , "PGK" ) aadd(::aValueList , "PYG" ) aadd(::aValueList , "PEN" ) aadd(::aValueList , "PHP" ) aadd(::aValueList , "XPT" ) aadd(::aValueList , "PLN" ) aadd(::aValueList , "QAR" ) aadd(::aValueList , "ROL" ) aadd(::aValueList , "RUB" ) aadd(::aValueList , "WST" ) aadd(::aValueList , "STD" ) aadd(::aValueList , "SAR" ) aadd(::aValueList , "SCR" ) aadd(::aValueList , "SLL" ) aadd(::aValueList , "XAG" ) aadd(::aValueList , "SGD" ) aadd(::aValueList , "SKK" ) aadd(::aValueList , "SIT" ) aadd(::aValueList , "SBD" ) aadd(::aValueList , "SOS" ) aadd(::aValueList , "ZAR" ) aadd(::aValueList , "LKR" ) aadd(::aValueList , "SHP" ) aadd(::aValueList , "SDD" ) aadd(::aValueList , "SRG" ) aadd(::aValueList , "SZL" ) aadd(::aValueList , "SEK" ) aadd(::aValueList , "CHF" ) aadd(::aValueList , "SYP" ) aadd(::aValueList , "TWD" ) aadd(::aValueList , "TZS" ) aadd(::aValueList , "THB" ) aadd(::aValueList , "TOP" ) aadd(::aValueList , "TTD" ) aadd(::aValueList , "TND" ) aadd(::aValueList , "TRL" ) aadd(::aValueList , "USD" ) aadd(::aValueList , "AED" ) aadd(::aValueList , "UGX" ) aadd(::aValueList , "UAH" ) aadd(::aValueList , "UYU" ) aadd(::aValueList , "VUV" ) aadd(::aValueList , "VEB" ) aadd(::aValueList , "VND" ) aadd(::aValueList , "YER" ) aadd(::aValueList , "YUM" ) aadd(::aValueList , "ZMK" ) aadd(::aValueList , "ZWD" ) aadd(::aValueList , "TRY" ) Return Self
WSMETHOD SOAPSEND WSCLIENT CurrencyConvertor_Currency Local cSoap := "" cSoap += WSSoapValue("Value", ::Value, NIL , "string", .F. , .F., 3 , NIL, .F.) Return cSoap
WSMETHOD SOAPRECV WSSEND oResponse WSCLIENT CurrencyConvertor_Currency ::Value := NIL If oResponse = NIL ; Return ; Endif ::Value := oResponse:TEXT Return
WSMETHOD CLONE WSCLIENT CurrencyConvertor_Currency Local oClone := CurrencyConvertor_Currency():New() oClone:Value := ::Value Return oClone
Agora, para consumir este serviço, precisamos dar uma olhada com calma na classe proxy gerada. Primeiro, vejamos os métodos disponíveis:
WSMETHOD NEW WSMETHOD INIT WSMETHOD RESET WSMETHOD CLONE WSMETHOD ConversionRate
Os quatro primeiros métodos fazem parte da manutenção da instância da classe client. Por hora o que nos interessa é o construtor (NEW), e o método ConversionRate, que é o único método disponibilizado por este serviço. Vamos procurar pela declaração deste método.
WSMETHOD ConversionRate WSSEND oWSFromCurrency,oWSToCurrency WSRECEIVE nConversionRateResult WSCLIENT WSCurrencyConvertor
Ao avaliarmos a implementação do método, vemos que ele têm dois parâmetros, do tipo Objeto, e um resultado numérico. Vamos procurar no fonte pela definição de cada um destes parâmetros (oWSFromCurrency e oWSToCurrency).
WSDATA oWSFromCurrency AS CurrencyConvertor_Currency WSDATA oWSToCurrency AS CurrencyConvertor_Currency
Cada um dos parâmetros do WebService é declarado como uma propriedade da classe Proxy. Neste caso, ambos são objetos do mesmo tipo ( CurrencyConvertor_Currency ). Vamos ver definição deste objeto:
// WSDL Data Enumeration Currency
WSSTRUCT CurrencyConvertor_Currency WSDATA Value AS string WSDATA cValueType AS string WSDATA aValueList AS Array Of string WSMETHOD NEW WSMETHOD CLONE WSMETHOD SOAPSEND WSMETHOD SOAPRECV ENDWSSTRUCT
Então, este objeto é um “Enumeration”, com base em uma String. Isto permite o código Client do Web Services verificar se o conteúdo informado é válido antes mesmo da requisição ser realizada.
Com estas informações, vamos montar o fonte Client em AdvPL que vai consumir esta classe.
#include "protheus.ch"
/* ------------------------------------------------------------------- Função U_MOEDAS Autor Júlio Wittwer Data 28/03/2015 Descrição Fonte de exemplo consumingo um Web Service publico de fator de conversão de moedas, utilizando a geração de classe Client de Web Services do AdvPL Url http://www.webservicex.net/CurrencyConvertor.asmx?WSDL ------------------------------------------------------------------- */ User Function Moedas()
Local oWS
// Cria a instância da classe client oWs := WSCurrencyConvertor():New()
// Alimenta as propriedades de envio oWS:oWSFromCurrency:Value := 'BRL' // Real ( Brasil ) oWS:oWSToCurrency:Value := 'USD' // United States Dollar
// Habilita informações de debug no log de console WSDLDbgLevel(3)
// Chama o método do Web Service If oWs:ConversionRate() // Retorno .T. , solicitação enviada e recebida com sucesso MsgStop("Fator de conversão: "+cValToChar(oWS:nConversionRateResult),"Requisição Ok") MsgStop("Por exemplo, 100 reais compram "+cValToChar(100 * oWS:nConversionRateResult )+" Dólares Americanos.") Else // Retorno .F., recupera e mostra string com detalhes do erro MsgStop(GetWSCError(),"Erro de Processamento") Endif
Return
Ao executar este fonte, o retorno esperado é uma sequência de duas janelas de informações, contendo o fator de conversão, e um exemplo de uso deste fator convertendo 100 reais para dólares americanos.
Agora, vamos ver como este exemplo seria escrito usando a classe tWSDLManager(), da forma mais minimalista possível.
#include "protheus.ch"
/* ------------------------------------------------------------------- Função U_MOEDAS2 Autor Júlio Wittwer Data 28/03/2015 Descrição Fonte de exemplo consumingo um Web Service publico de fator de conversão de moedas, utilizando a classe tWSDLManager() Url http://www.webservicex.net/CurrencyConvertor.asmx?WSDL ------------------------------------------------------------------- */
User Function Moedas2() Local oWSDL Local lOk, cResp, aElem, nPos
oWSDL := tWSDLManager():New()
// Seta o modo de trabalho da classe para "verbose" oWSDL:lVerbose := .T.
// Primeiro faz o parser do WSDL a partir da URL lOk := oWsdl:ParseURL( "http://www.webservicex.net/CurrencyConvertor.asmx?WSDL" ) if !lOk MsgStop( oWsdl:cError , "ParseURL() ERROR") Return endif
// Seta a operação a ser utilizada lOk := oWsdl:SetOperation( "ConversionRate" ) if !lOk MsgStop( oWsdl:cError , "SetOperation(ConversionRate) ERROR") Return endif
// Setar um valor para conversão
lOk := oWsdl:SetFirst('FromCurrency','BRL') if !lOk MsgStop( oWsdl:cError , "SetFirst(FromCurrency) ERROR") Return endif
lOk := oWsdl:SetFirst('ToCurrency','USD') if !lOk MsgStop( oWsdl:cError , "SetFirst (ToCurrency) ERROR") Return endif/
// Faz a requisição ao WebService lOk := oWsdl:SendSoapMsg() if !lOk MsgStop( oWsdl:cError , "SendSoapMsg() ERROR") Return endif
// Recupera os elementos de retorno, já parseados cResp := oWsdl:GetParsedResponse()
// Monta um array com a resposta parseada, considerando // as quebras de linha ( LF == Chr(10) ) aElem := StrTokArr(cResp,chr(10))
nPos := ascan(aElem,{|x| left(x,21) == 'ConversionRateResult:'}) If nPos > 0 nFator := val( substr(aElem[nPos],22) ) MsgStop("Fator de conversão: "+cValToChar(nFator),"Requisição Ok") MsgStop("Por exemplo, 100 reais compram "+cValToChar(100 * nFator )+" Dólares Americanos.") Else MsgStop("Resposta não encontrada ou inválida.") Endif
Return
Para utilizar este exemplo, é necessário criar um arquivo para conter o Fonte Client da classe AdvPL gerada, e outro para compilar o fonte que consome esta classe client, e para executar, basta chamar os fontes U_Moedas e U_Moedas2 a partir do SmartClient.
A classe tWSDLManager, embora seja superior em relação ao SOAP, ainda precisa de algumas informações adicionais para se tornar mais prática para uso. Para montar o exemplo acima, foi necessário codificar um fonte um pouco maior, chamando os métodos adicionais da classe para obter os nomes dos serviços publicados, os nomes dos elementos de parâmetro e retorno do serviço desejado, e verificar o formato de retorno parseado para extrair o valor retornado pelo Web Service.
Nos próximos tópicos sobre Web Services, vamos abordar entre outras coisas a utilização de serviços que requerem autenticação e/ou conexão segura (SSL) utilizando chaves criptográficas, e como tratar de forma adequada as falhas que podem ocorrer no lado de uma aplicação client de Web Services.
Conclusão
Este post foi só pra abrir o apetite, têm muito mais a ser explorado neste universo. Mais uma vez, agradeço a todos pela audiência do Blog, e espero vê-los por aqui mais vezes. Até o próximo post, pessoal 😉
Perfeito.
Muito obrigado, Julio.
CurtirCurtido por 1 pessoa
Opa, magina 😉 Não por isso 😀
CurtirCurtir
Excelente artigo! Vou ficar no aguardo dos próximos!
Só fiquei com uma dúvida, como o fonte U_Moedas irá conseguir instanciar o objeto da classe
WSCurrencyConvertor sendo que não estamos mais utilizando aquele fonte gerado automáticamente? A classe tWSDLManager irá se encarregar internamente da criação dela?
Se possível gostaria de sugerir um artigo explicando o REST também 🙂
Pelo que vi o Fluig esta utilizando o conceito de REST.
Obrigado por compartilhar! Abraços!!
CurtirCurtido por 1 pessoa
Obrigado 😉
Na verdade a classe tWSDLManager atua como proxy, uma vez que você carregue o WSDL, ela interpreta o WSDL e permite você selecionar os métodos, escolher o método a ser chamado, inputar os parâmetros e fazer a requisição. Porém, cada vez que você for chamar um método do WebService, você precisa fornecer o WSDL, via URL ou por arquivo em disco, pelo menos para fazer a primeira chamada, depois você pode manter a instância na memória e selecionar outros métodos deste Web Service para serem chamados. O melhor dos mundos vai ser quando a tWSDLManager for utilizada internamente para a geração de uma classe estática, aí sim vai ficar mais simples desenvolver aplicações client para Web Services no AdvPL.
Quanto ao REST, ele está na lista de tópicos a serem abordados em breve 😀
Abraços
CurtirCurtir
Testei a classe e funcionou perfeitamente. Vou começar a utilizá-la nas minhas implementações futuras.
Valeu!!
CurtirCurtido por 1 pessoa
Tenho alguns projetos na empresa que já usam webservice. Gosto muito da abordagem e da flexibilidade do serviço, apesar de achar que muita coisa poderia ser melhorada. Tenho pouca experiência no desenvolvimento de webservices em ADVPL, mas a empresa já sinalizou que novos projetos virão nesta área, então qualquer boa dica será bem vinda!
Estou aguardando os próximos posts com muita ansiedade!
CurtirCurtido por 1 pessoa
Olá Thais,
Este assunto será abordado em maior profundidade, esta semana o post está “atrasado” devido a questões parelelas …rs… o fluxo normal do Blog deve retornar na semana que vem !
Grato pela audiência 😀
CurtirCurtir
Siga0984,
Estou pesquisando e não encontro informação sobre um determinado assunto, qual o local mais adequado para eu ter fazer uma pergunta?
CurtirCurtir
Opa, perfeitamente ! Mande um e-mail para siga0984@gmail.com 😀
CurtirCurtir
Olá… um artigo muito atual, bem feito e esclarecedor…. parabéns.
Mas vc saberia me dizer pq no protheus q utilizo ele dá “invalid class TWSDLMANAGER” quando tento instanciar ela?
CurtirCurtido por 1 pessoa
Olá Marcel, obrigado 😀
Verifique a Build do Protheus Server que você está utilizando. Esta classe somente está disponível a partir da Build 7.00.131227A
Abraços
CurtirCurtir
Júlio, mais uma vez parabéns!
Tenho um processo de integração desenvolvido que utiliza a classe WSCLIENT. Monitorando o console do servidor percebi algumas mensagens com esse conteúdo..” Too many XML dinamic nodes This may slow down parser operations”. Isso significa que o processo está sofrendo na performance…pergunta: Converter para a classe TWSDLMANAGER resolve?
CurtirCurtido por 1 pessoa
Olá Luiz, obrigado 😉
Na verdade, existem duas possibilidades disso acontecer, todas relacionadas ao uso da função XmlParser() e XmlParserFile(). Como você utiliza uma classe client de WebServices gerada pelo Advpl, que iternamente utiliza a XmlParser(), isto pode acontecer quando um XML retornado por uma requisição de WebServices têm muitos nós internos, OU , não está sendo feita nenhuma limpeza dos nós de XML. Apos utilizar a XmlParser() e/ou XmlParserFile(), ocorre a criação dinâmica do objeto XML pelo parser. Após você utilizar os objetos dinâmicos gerados pelo parser, a definição deles continua na memória, a menos que você limpe a definição usando a função DelClassIntf() . Ela está documentada na TDN, mas a documentação não está refletindo o seu comportamento, e precisa ( será ) revisada. 😉
Abraços
CurtirCurtir
E, a propósito, se você fizer um parser XML usando a classe tXmlManager(), ela não gera classes dinâmicas, logo ela não têm este problema. Esta classe é bem mais leve e mais rápida para fazer o parser 😀
Abraços
CurtirCurtir
Boa tarde! Parabéns pelos exemplos, são de grande ajuda, consegui fazer uma requisição para um webservice de terceiro usando a classe TWSDLMANAGER . Porém me deparei com um problema, tenho que fazer uma requisição para um webservice no qual posso enviar vários ítens em uma mesma requisição(um array), porém consigo enviar apenas um. Você tem algum exemplo deste tipo na classe TWSDLMANAGER pra me enviar? Solicitei na TOTVS e infelizmente eles não mandam, falaram pra utilizar os exemplos do TDN, que são horríveis.
Abraço,
Felippe
CurtirCurtido por 1 pessoa
Rapaz, ainda não montei um exemplo desses, mas aproveitando a necessidade, é uma boa idéia. Vou montar um exemplo desses no próximo post 😀
Abraços
CurtirCurtir
Obrigado pelo Post!
A tempos tava procurando algo muito bem explicado!
Parabéns!
CurtirCurtido por 1 pessoa
Olá Bruno, obrigado 😉
CurtirCurtir
Cara, Salvou o meu dia aqui, estava tendo estouro de memória direto, estou processando muitos registros e acaba dando erro.
CurtirCurtido por 1 pessoa
Olá um artigo muito bom, parabéns.
Estou precisando de um exemplo usando Restful, o exemplo que tem no TDN está difícil de entender.
Já criei o ambiente “Microsiga Protheus – RESTFul API” com base no exemplo criei a classe com os métodos porém não consigo consumir nada.
Att,
CurtirCurtido por 1 pessoa
Opa, obrigado ! O REST também está na lista de tópicos a serem abordados, acompanhe os posts que logo sai um sobre ele ! 😀 Abraços 😉
CurtirCurtir
Olá! td bem?
Deixa eu te perguntar. O ADVPL aceita URL dinamica? Tenho um projeto que preciso consumir URL’s dinamicas. O projeto que preciso consumir foi feito .net, através do REST, ele cria URL dinamicas.
Ex: Pra eu atualizar clientes eu preciso fazer:
http://sitedocliente.com.br/api/clientes/1014/atualizar
Sendo que o 1014 é o código do cliente.
Desde já obrigado!
Abrs
Fernando
CurtirCurtido por 1 pessoa
Boa tarde,
Muito obrigado Julio , você nem imagina o valor deste post.
Att.
CurtirCurtido por 1 pessoa
Olá João Paulo ! Magina …rs.. 😉
CurtirCurtir
Grande Julião,
Não sei se irá se recordar de mim, pois estava sempre com o Mansano e o Rodrigo. Parceiro, você tem como dar aula, montar umas turmas se tiver disponibilidade claro, sobre webservice, MVC, incluindo este webret e TWsdlManager que não se encontra e é exigido nas vagas. Estou perdendo algumas oportunidades, principalmente devido webret e TWsdlManager.
Cara fico no aguardo de seu retorno.
Abraços
CurtirCurtido por 1 pessoa
Fala Valdemir, seu nome não me soa estranho ..rs.. mas ainda não “liguei” o nome à pessoa 😉 De qualquer modo, já me passou pela cabeça dar aulas de AdvPL … porém preciso criar o material, e separar tempo para isso, e verificar se isso não fere meu contrato de trabalho com a TOTVS. 😉 MVC , REST e afins são implementações do Framework, preciso aprofundar um pouco mais meus conhecimentos nestes assuntos para estar apto a dar aula sobre isso 😀
Manda um e-mail pra mim (siga0984@gmail.com), vamos falar mais sobre isso !!
Abraços
CurtirCurtir
Excelente post… me animou bastante nos estudos iniciais em webservice em advpl.
Consegui rodar sem problemas as funções U_Moedas e U_Moedas2 porém não compreendi muito bem a relação com o fonte webservice criado no TDS que também foi compilado no meu ambiente.
Abraços….
CurtirCurtido por 1 pessoa
Bom dia, é possível utilizar um WebService para dar carga em uma rotina MSExecAuto do Protheus 12?
Já criei a rotina execauto que irá imputar os dados no banco mas não sei como proceder para que um webservice busque um arquivo TXT ou XML em determinado diretorio com os dados e tais informações sejam processadas pela função autoexec criada e grave esses dados no banco de dados. Alguém poderia me dar uma luz?
Desde já agradeço.
Sidelcino Souza
CurtirCurtido por 1 pessoa
Olá Sidelcino, boa noite,
Acredito que sua dúvida seja melhor atendida em algum fórum sobre AdvPL e ERP, como por exemplo o HelpFacil, ou outro fórum disponivel em algum dos “links interesantes” na barra lateral direita do Blog 😀
Abraços
CurtirCurtir
Olá!
Estou utilizando um Webservice SOAP Protheus que retorna dados do Easy Import Control, porém nos testes, quando a mensagem ultrapassa 1 (um) megabyte, o servidor retorna:
document.cookie = ”
Sabes qual parâmetro deve ser alterado? Já passaste por este problema?
Atenciosamente.
CurtirCurtido por 1 pessoa
Recentemente o APPServer ganhou uma nova configuração para aumentar o tamanho maximo de string no Advpl para mais de 1 MB. Dê uma olhada e teste a configuração MaxStringSize ( http://tdn.totvs.com/display/tec/MaxStringSize )
Abraços
CurtirCurtir
Bom dia Julio.
Necessitamos desenvolver um processo de comunicação webservice no qual o XML deve ser assinado digitalmente utilizando certificado A1 ou A3.
Preciso saber se o ADVPL do Protheus 12 tem funcionalidades disponíveis para que possamos desenvolver este processo.
Você tem esta informação?
E abusando um pouco mais, se for possivel você teria algum material apenas para me dar um “norte”?
Já abri chamado por duas vezes no portal da Totvs más não recebo nenhuma resposta.
Att.
CurtirCurtido por 1 pessoa
Marllon, boa noite,
Existe na TDN alfumas funções documentadas justamente para assinar XML, vide link http://tdn.totvs.com/display/tec/Criptografia — Funções *RSA, para assinatura de conteudo digitalmente.
Espero ter ajudado !!!
CurtirCurtir
Parabéns, muito bom o teu post
CurtirCurtido por 1 pessoa
Tks 😉
CurtirCurtir
Olá Júlio, estou com uma dificuldade quando a decimais no envio da função WSSOAPVALUE:
Olhe o exemplo abaixo onde a variável npriceValue é 139.41 (2 casas decimais):
Local cSoap := “”
cSoap += WSSoapValue(“priceTp”, ::cpriceTp, ::cpriceTp , “string”, .F. , .F., 0 , NIL /*”br.com.accurate.acpr.priceservices”*/, .F.)
cSoap += WSSoapValue(“priceValue”, ::npriceValue, ::npriceValue , “decimal”, .T. , .F., 0 , NIL /*”br.com.accurate.acpr.priceservices”*/, .F.)
O conteúdo de cSoap fica com: “DFL139.41000000”
Veja que quando eu retorno ele pela função WSSoapValue ele preenche com 6 zeros à direita.
Alguma sugestão para correção, pois a documentação do integrador obriga que tenha apenas 2 casas decimais e passaram a bloquear o conteúdo.
CurtirCurtido por 1 pessoa
Opa, bom dia !
Então … A função WSSoapValue() — responsável por criar o nó XML automaticamente a partir do valor AdvPL e do tipo declarado no WSDL — não tem parametrização de precisão decimal … MAS, existe um contorno … Altere o código client gerado para você injetar a Tag XML com o formato esperado … sem usar a WSSoapValue …Por exemplo:
cSoap += ” + alltrim(str(::npriceValue,2)) + ”
😀
CurtirCurtido por 1 pessoa
Entendi, então, acabei remediando por motivo de urgência nesse formato.
StrTran(WSSoapValue(“priceValue”, ::npriceValue, ::npriceValue , “decimal”, .T. , .F., 0 , NIL /*”br.com.accurate.acpr.priceservices”*/, .F.),”000000″,””)
Gambiarra!
Poderia colocar nesse formato abaixo?
WSSoapValue(“priceValue”, Alltrim(str(::npriceValue,2)), Alltrim(str(::npriceValue,2)) , “decimal”, .T. , .F., 0 , NIL /*”br.com.accurate.acpr.priceservices”*/, .F.)
CurtirCurtido por 1 pessoa
Corrigindo (trocar o decimal por string:
WSSoapValue(“priceValue”, Alltrim(str(::npriceValue,2)), Alltrim(str(::npriceValue,2)) , “string”, .T. , .F., 0 , NIL /*”br.com.accurate.acpr.priceservices”*/, .F.)
CurtirCurtido por 1 pessoa
Sim, eu acho que serviria também ..rs… e fica mais “elegante” 😀
CurtirCurtir
Olá Júlio, boa noite tudo bem? Eu tenho link WSDL (https://api.angellira.com.br/ws-soap/WSImport.asmx?WSDL) de um WebService que desejo consumir. Gerei o client dele e compilei. Nesse WS, tem um método que estou querendo usar chamado “AgendarViagem”. Para consumi-lo, eu criei uma User Function, instanciei a classe CLIENT do WS, chamei o método, passei os parâmetros necessários e não acontece nada! Não consigo obter nenhum retorno. E o mais engraçado é que quando eu testo esse método pela ferramenta SOAP UI ele roda e retorna o XML com os dados direitinho! E o mesmo envelope que eu passei via SOAP UI é que eu estou passando na minha função.
Será que você poderia me ajudar?
Além disso, conversando com uns colegas aqui e ali, me disseram que quando no WSDL do WS existem ‘complexType mixed=”true”‘ o VSCode ou TDS não consegue gerar o CLIENT de forma completa. Tem isso mesmo? Qual sua opinião?
CurtirCurtido por 1 pessoa
Olá Marcos,
Parece que o WSDL fornecido usa algumas coisas mais “rebuscadas” para criar a estrutura de retorno… o Retorno de alguns métodos são extensões ou complementos do objeto de retorno da inclusão, e coisas assim — complex type mixed // any nao oferecem uma descrição que permita “mastigar” o XML retornado pela requisição, fazendo as propriedades de envio serem criadas com o tipo “as SCHEMA” … e a parte de retorno com mixed type realmente não conseguem lidar com isso ..
Neste caso, existem 3 caminhos : Alterar o fonte da classe client de WS gerada, para pegar os dados necessários ( Caso eles estejam dentro do objeto XML de retorno), ou implementar um client de webservices usando a nova classe tWSDLManager — ( com suporte a mais recursos do WSDL / SOAP e sem a necessidade de geração de fonte), ou ainda a 3a opção: Fazer a API cliente de webservices na unha, fazendo POST do XML e parser do retorno “na mão”….
Espero que estas informações lhe sejam úteis !! Abraços 😀
CurtirCurtir
Bom dia
Como faria para usar autenticação token em SOAP ADVPL?
CurtirCurtido por 1 pessoa
Opa, beleza ? Olhe .. a aplicação Client AdvPL para webservices cria uma classe client , chamada “_HEADOUT” … Ela é um array, e nele voce pode acrescentar uma ou mais strings, que serao enviadas no Header http da requisição montada na hora da chamada do metodo da classe client.
O Token em si, e a forma de colocar ele no header, depende do que o serviço WebServices Server exige . Existem autenticações de token feitas para serviços AdvPL do ERP, implementados pelo framework. Se for esse o caso, dê uma mergulhada no TDN, ok ? 😀
CurtirCurtir