sexta-feira, 10 de fevereiro de 2017

REST & SOAP

Temos ha algum tempo dois modelos de conexão a webservices implementados no Genexus a sua disposição para que inicie seus projetos: SOAP (Simple Object Access Protocol) e o REST (Representational State Transfer). O primeiro um protocolo de comunicação e o segundo um modelo de conexão entre sistemas. Esquisito? sim e muito, veja aqui as diferenças de implementação e uso.

Objetos Tipo Webservices

Primeiramente, Genexus permite que vários de seus objetos atuem como webservice, de forma que podemos implementar cenários bastante interessantes:
  • Transaction: permite fazer com que o CRUD (Create, Read, Update, and Delete) de uma Transação opere como webservice. Em outras palavras, podemos disponibilizar serviços que façam o Insert, Update e Delete de registros em tabelas a partir da porção Business Component da transação.
  • Procedures: poderíamos implementar regras de negócio mais especificas para realizar tanto a operação de manutenção das tabelas, como também para devolver informações que deveriam passar por algum tipo de processamento.
  • Data Provider: permite-nos criar um recurso que facilmente recupera informações das tabelas, podendo gerar 'views dinâmicas', que associados aos webservices podem alimentar sistemas externos famintos por informação.
Desta forma, a ferramenta suporta, digamos, com o padrão Genexus de fazer as coisas ficarem mais simples, uma ampla gama de possibilidades quando pensamos nos tais webservices.

REST & SOAP

Ja apresentamos alguns exemplos aqui no Genexando a respeito da aplicação e consumo de webservices SOAP, principalmente devido a sua simplicidade de construção e uso em Genexus. Pois para consumir basta utilizarmos uma ferramenta que aponta para o WSDL do serviço e que cria automaticamente um objeto externo que aponta para a execução da procedure remota, esta ferramenta é chamada WSDL IMPORT. Para construir, basta pegar uma procedure qualquer e marcá-la com as opções Main Program = true e Call Protocol = SOAP.

Os webservices do tipo SOAP são mais simples de utilizar porque, no Genexus, estão associados a um objeto externo. Podemos encarar a coisa mais ou menos assim, imagina que uma procedure está em uma Kb e foi pubicada como SOAP, em outra Kb, quando apontamos para este SOAP com o WSDL Import, o Genexus criará um objeto (External Object) com o mesmo nome desta procedure 'externa', permitindo que executemos esta procedure. Seria algo como, tornar uma procedure, que inicialmente encontra-se sob o escopo privado em uma Kb, como pública e acessível para outras Kbs ou mesmo sistemas externos, não Genexus.

Agora quando falamos de REST a coisa muda um pouquinho. Praticamente o que temos é estilo para chamar recursos na Web mediante a definição da operação na URI, mas deixemos as discussões quanto às diferenças para os filósofos da Internet, melhor focar na implementação do Genexus para ambos os recursos.

Genexus publica ambos os modelos para que possam ser acessados mediante chamadas HTTP ou HTTPS, SOAP vai se comunicar utlizando XML, e REST é mais flexível, permitindo que a transferência de informações ocorra tanto com XML, JSON, Texto, ou mesmo outro modelo qualquer.

Em ambos os cenários a questão da segurança é muito importante, porque se estamos transferindo algo valioso entre sistemas, pela Web, com certeza algum bisbilhoteiro vai tentar interceptar a comunicação. Desta forma se você não é adepto a certificados digitais SSL, esqueça este recurso, isto aqui não é para você. Bom, na verdade, as credenciais dos seus usuários neste momento também estão expostas, neste caso.

Programando REST

Para entender um pouco mais da programação webservice Genexus com SOAP, recomendo a leitura do artigo http://www.genexando.com/2009/07/web-services.html, em que já apresentamos um exemplo sobre este modelo.

Quanto ao REST necessitamos explorar um pouco deste tema aqui no Genexando, pois o modo de programação é bastante diferente.

1) Segurança é tudo!

Primeiramennte é necessário entender um pouco a respeito da segurança da chamada ao serviço, e no caso do Genexus o GAM sempre é uma boa opção para proteger tudo.

No GAM é necessário realizar um login na aplicação, controlado pelo protocolo OAuth implementado no recurso, e este cria um Token assim que se estabelece a conexão. Por meio deste Token será possível realizar as próximas conexões com o servidor sem que se tenha de reenviar o usuário e senha. Por isso a importância do protocolo SSL, pois a passagem dos parâmetros, principalmente neste momento deve estar completamente encriptada e protegida. GAM com http não vai adiantar nada!

Para procedermos é necessário fazer uso do tipo HttpClient do Genexus que permite configurar os parâmetros para a execução do processo, que deve acontecer pela chamada POST ao serviço /oauth/access_token. A seguir um exemplo de login no GAM.

Event 'login'
  &httpclient.Host = 'localhost/'
  &httpclient.Port = 80
  &httpclient.BaseUrl = '/Kb.NetEnvironment/oauth/'
  &addstring ='client_id=068868de058b4798bd3b90f696fa37f8&grant_type=password&scope=FullControl&username=admin&password=admin123'

  &httpclient.AddHeader("Content-Type", "application/x-www-form-urlencoded") 
  &httpclient.AddString(&addstring)
  &httpclient.Execute('POST','access_token')
 
  &json = &httpclient.ToString()
  &AccessTokenSDT.FromJson(&json)

  &websession.set('TOKEN', &AccessTokenSDT.access_token)
EndEvent

O valor de client_id informado deve ser obtido no GAM, na área Applications - Client Application Data.


O retorno da chamada é no formato JSON, desta forma é necessário converter o texto recebido em um SDT. No nosso exemplo, algo como:


Um exemplo de retorno da chamada deste serviço seria algo como:

{
 "access_token":"54f03097-e105-4bfd-9ff4-eba57KM6hSB39vFRlA7a4iAE",
 "scope":"FullControl",
 "refresh_token":"",
 "user_guid":"985ee5fe-8d5c-4008-8a0b-050beb6bddb0"
}

A estratégia do exemplo é armazenar o Token na websession, para em seguida obtê-lo quando necessário.


2) Inserindo registros

Considerando uma transação Empresa que foi definida como REST.


Poderemos realizar operação de inserção de registros por meio do serviço, que se encontrará disponibilizado na URL a seguir, observe que se deve incluir um /rest e o /Empresa é a transação, que não precisamos ascrescentar a extensão aspx (se for .Net)

localhost/Kb.NetEnvironment/rest/Empresa

A sequencia de ações para se chamar este serviço REST é a seguinte:
  1. Definir o caminho ao serviço
  2. Incluir no Header da mensagem o Token do GAM
  3. Incluir no corpo da mensagem o registro a  ser inserido
  4. Definir a chave primária do registro a ser inserido
  5. Executar o POST

A primeira ação é a operação de conexão ao servidor, que definimos em uma sub rotina chamada Host. Observe que é necessário passar o Token obtido no login anteriormente apresentado, incluíndo-o no cabeçalho da chamada.

Sub 'Host'
  &httpclient.Host = 'localhost/'
  &httpclient.Port = 80
  &httpclient.BaseUrl = '/ Kb.NetEnvironment/rest/'
  &httpclient.AddHeader('Authorization','OAuth ' + &websession.get('TOKEN'))
EndSub

Em seguida,  será necessário incluir o registro a ser inserido no corpo da mensagem, através do &httpclient.AddString(&EmpresaSDT.ToJson()). Desta forma, deveremos ter um objeto EmpresaSDT para definir a estrutura da informação.


Falaremos do gx_md5_hash logo abaixo.

Um detalhe importante na chamada é a passagem do Id do registro, no caso EmpresaId, que para o insert, deve ser zero. Ou seja, temos como enviar parâmetros para a regra Parm do objeto.

O Execute no método POST realiza a chamada ao serviço.

Event 'Insert'
  do 'Host'
  &httpclient.AddHeader('content-type','application/json')
  &httpclient.AddString(&EmpresaSDT.ToJson())
  &querystring='Empresa' + "/" + &EmpresaSDT.EmpresaId.ToString()
  &httpclient.Execute('POST', &querystring)
  &StatusCode=201
  do 'Message'
Endevent

Para tratar o resultado temos o código de retorno 201 sendo o StatusCode correto para a operaçao, ou seja, se acontecer o registro foi inserido corretamente.

Sub 'Message'
  if &httpclient.StatusCode=&StatusCode
    msg("Informação atualizada")
  else
    msg("Ocorreu um erro.." + &httpclient.ToString())
  endif
EndSub


2) Atualizando registros

A atualização é muito semelhante ao processo de inserção, com apenas um pequeno detalhe, será necessário obter primeiro o registro que se encontra na tabela. Isto é feito para que o Genexus realize o controle de concorrência no processo de atualização.

Sendo assim, a carga do registro deve ser feita por uma chamada GET ao serviço, a seguir um exemplo.

Event 'get'
  do 'Host'
  &httpclient.AddHeader('content-type','application/json')
  &querystring='Empresa' + "/" + &EmpresaSDT.EmpresaId.ToString()
  &httpclient.Execute('GET',&querystring)
  &EmpresaSDT.FromJson(&httpclient.ToString()) 
  &StatusCode=200
  do 'Message'
Endevent

O registro deve ser manipulado a partir de uma variável SDT, e em seguida sua atualização deve ser enviada ao servidor chamando-se um método PUT, conforme o modelo a seguir. Em todo processo será necessário informar a chave primária do registro.

Event 'update'
  if &EmpresaSDT.gx_md5_hash.IsEmpty()
    msg('Do a get first')
  else
    do 'Host'
    &json=&EmpresaSDT.ToJson()  
    &httpclient.AddHeader('content-type','application/json')
    &httpclient.AddString(&json) 
    &querystring='Empresa' + "/" + &EmpresaSDT.EmpresaId.ToString()
    &httpclient.Execute('PUT', &querystring)
    &StatusCode=200
    do 'Message'
 endif
EndEvent

O detalhe importante neste processo é a necessidade de se armazenar um código Hash gerado pelo Genexus para controlar a concorrência, obtido no GET e enviado para o servidor no PUT, em seguida. Este hash se altera a cada operação de consulta ao banco.


Este Hash é guardado no SDT na propriedade gx_md5_hash, toda comunicação que venha atualizar o mesmo deverá informar esse valor.

3) Apagando registros

Finalmente, para se apagar o registro precisaremos de uma simples chamada DELETE, conforme o exemplo apresentado a seguir.

Event 'delete'
 do 'Host'
 &querystring='Empresa' + "/" + &EmpresaSDT.EmpresaId.ToString()
 &httpclient.Execute('DELETE', &querystring)
 &StatusCode=200
 do 'Message'
Endevent

Resumindo

Podemos utilizar uma transação e implementar um mecanismo de operação remota dos registros publicando-a como REST.  O próprio Genexus utiliza esta estratégia para prover informações e funcionalidades para as aplicações SD.

O mecanismo torna o modelo de programação um pouco mais cheia de código, porém, oferece novos cenários de aplicação.

O que se deve levar em conta é a velha história do desempenho x controle, pois neste modelo teremos um forte controle sobre o compartilhamento da informação, por outro lado, teremos um novo cenário no qual não poderemos explorar muito o desempenho do acesso direto ao banco de dados. Para compensar, o modelo vai exigir um pouco mais de infra-estrutura para compensar a pequena ineficiência.

Particularmente, acho interessante.

Mais informação em:
Acesso aos exemplos, formato XPZ: