Trabalhando com Webservice Rest ADVPL

porPaulo Henrique Corrêa Cardoso

Trabalhando com Webservice Rest ADVPL

Já pensou em integrar suas aplicações com o ERP Protheus ? O Protheus, como vários ERP’s, são sistemas muito completos e que abrange diversas áreas da empresa, porém sabemos que muitos desses ERP’s não tem acompanhado as novas aplicações de mercado, principalmente na área web/mobile. Mas enquanto as grandes desenvolvedoras adequam seus ERP’s para essa nova geração tecnológica, podemos muito bem fazer os grandes e arcaicos ERP’s conversarem com ferramentas mais amigáveis, dessa forma unimos o melhor de cada uma das tecnologias para otimizar os trabalhos. O Protheus contempla algumas formas de realizar integração, como: Webservice SOAP EAI Próprio Webservice Rest Em minha concepção o mais aderente as plataformas existentes no mercado é o webservice Rest que possui sua representação em JSON e por padrão TOTVS já possui sua camada interface e de autenticação bem implementadas. Passos necessários para preparar seu Protheus para funcionar com o Webservice Rest:

Configurar um server de webservice Rest

Para realizar a configuração será necessário adicionarmos alguns itens no arquivo .ini do server Protheus. Sugiro que seja criado um server separado para o Webservice, tanto por questão de performance, como também por questão de manutenção.
[GENERAL]
MAXSTRINGSIZE=10
 
[HTTPV11]
Enable=1
Sockets=HTTPREST

[HTTPREST]
Port=8080
URIs=HTTPURI
SECURITY=1

[HTTPURI]
URL=/rest
PrepareIn=All
Instances=1,2

[ONSTART]
jobs=HTTPJOB
RefreshRate=30

[HTTPJOB]
MAIN=HTTP_START
ENVIRONMENT=environment
 

Desenvolvendo sua Classe Rest ADVPL

Para começarmos a desenvolver nossa Primeira classe Rest em ADVPL vamos precisar entender um pouco a ideia dos verbos existentes nesta representação:
  • POST – Verbo responsável por realizar inclusões de registros no sistema.
  • PUT – Verbo  responsável por realizar alterações de registros no sistema.
  • GET – Verbo responsável por realizar o retorno de registros do sistema.
  • DELETE – Verbo  responsável por realizar exclusões de registros do sistema.
Definidas as responsabilidades de cada método podemos começar a exemplificar cada um deles. Para isso, primeiro precisamos a construir a estrutura base do nosso fonte ADVPL para nossa classe Rest.
#Include 'Protheus.ch'
#Include 'RestFul.CH'    //Necessario utilizar a incluide do RESTFUL

/*/-----------------------------------------------------------
{Protheus.doc} WsRstExp()
Dummy function da Classe Rest
Esta função é necessario pois o ADVPL necessita de um function
dentro do fonte 
Uso: WsRstExp

@sample
//U_WsRstExp()

@author Paulo Henrique Corrêa Cardoso.
@since 21/07/2020
@version 1.0
-------------------------------------------------------------/*/
User Function WsRstExp()
Return

/*/-----------------------------------------------------------
{Protheus.doc} WsRstExp()
Definição da Classe, propriedades e assinatura dos métodos
Uso: WsRstExp

@sample
//U_WsRstExp()

@author Paulo Henrique Corrêa Cardoso.
@since 21/07/2020
@version 1.0
-------------------------------------------------------------/*/
WSRESTFUL WsRstExp DESCRIPTION "Serviço REST de exemplo"
    WSDATA param1    As STRING
    WSDATA param2    As STRING Optional

    WSMETHOD GET    DESCRIPTION "Get Exemplo"     WSSYNTAX "/WsRstExp?param1={valueParam1}&param2={valueParam2}"
    WSMETHOD POST   DESCRIPTION "Post Exemplo"    WSSYNTAX "/WsRstExp"
    WSMETHOD PUT    DESCRIPTION "Put Exemplo"     WSSYNTAX "/WsRstExp?param1={valueParam1}&param2={valueParam2}"
    WSMETHOD DELETE DESCRIPTION "Delete Exemplo"  WSSYNTAX "/WsRstExp?param1={valueParam1}&param2={valueParam2}"

END WSRESTFUL
No exemplo acima definimos a classe WsRstExp, na sequencia definimos 2 parâmetros sendo um deles como opicional. Em seguida definimos a assinatura dos 4 métodos que serão utilizados. Na definição da assinatura temos a seguinte estrutura: WSMETHOD <cVerb> [cId] DESCRIPTION <cDescription> [WSSYNTAX <cSintax>] [PATH <cPath>] [TTALK <cTTalkVersion>] Na definição do método temos a seguinte estrutura: WSMETHOD <cVerb> [cId] [QUERYPARAM <QueryParms>] [PATHPARAM <PathParms>] [HEADERPARAM <HeaderParms>] WSRESTFUL <WsRestFul>
Nome Tipo Descrição Obrigatório
cVerb POST, PUT, GET ou DELETE X
cId Caracter ID para diferenciar e possibilitar a criação de métodos que utilizam verbos http repetidos
cDescription Caracter Descrição do método REST X
cSintax Caracter Sintaxe HTTP da chamada REST. Esta informação é utilizada apenas para documentação do REST.
cPath Caracter Definição do endpoint que irá acionar aquele método.
*Pode conter agrupamento, o nome da classe e os pathparms. (A partir da release 12.1.23 da lib, em jan./2019).
cTTalkVersion Caracter Valor “v1” para sinalizar que o método utiliza o padrão de mensagem de erro do TTALK.
QueryParms Indica os parâmetros, separados por vírgulas, que este método receberá via QueryString.
O parâmetros indicados aqui devem ser declarados como WSDATA.
PathParms Indica os parâmetros, separados por vírgulas, que este método receberá via path, ou seja, como parte da URL.
HeaderParms Indica os parâmetros, separados por vírgulas, que este método receberá via Header na requisição HTTP.
WsRestFul Caracter Indica o nome da classe, do serviço, que o método atual pertence. X
Agora vamos começar a definir os métodos logo abaixo do código anterior:
/*/-----------------------------------------------------------
{Protheus.doc} GET
Get de Exemplo - Método utilizado para consultas
Uso: WsRstExp
@sample
//GET / WsRstExp
@author Paulo Henrique Corrêa Cardoso.
@since 21/07/2020
@version 1.0
-------------------------------------------------------------/*/
WSMETHOD GET WSRECEIVE param1, param2 WSSERVICE WsRstExp
   Local lRet      := .T.         // Recebe o Retorno 
   Local oJsonRet  := NIL         // Recebe o JSON de Saida
   
    /*
   
  Trecho com a sua codificação
   
   */
  
   // Monta Objeto JSON de retorno
   oJsonRet := NIL
   oJsonRet := JsonObject():new()

   oJsonRet['Propriedade1'] := EncodeUTF8("Retorno 1", "cp1252") 
   oJsonRet['Propriedade2'] := 10 
   oJsonRet['Propriedade3'] := .T.
  
   // Devolve o retorno para o Rest
   ::SetResponse(oJsonRet:toJSON())
   
Return lRet


/*/-----------------------------------------------------------
{Protheus.doc} POST
Post de Exemplo - Método utilizado para inclusões
Uso: WsRstExp
@sample
//POST/ WsRstExp
@author Paulo Henrique Corrêa Cardoso.
@since 21/07/2020
@version 1.0
-------------------------------------------------------------/*/
WSMETHOD POST WSRECEIVE nullparam WSSERVICE WsRstExp
   Local lRet      := .T.         // Recebe o Retorno 
   Local cBody	   := ''          // Recebe o conteudo do Rest
   Local oJson     := NIL         // Recebe o JSON de Entrada
   Local oJsonRet  := NIL         // Recebe o JSON de Saida
   
   // Pega o conteudo JSON da transação Rest
   cBody := ::GetContent()
   ::SetContentType("application/json")

   oJson := JsonObject():new()
   oJson:fromJson(cBody)
  
   /*
   
  Trecho com a sua codificação
   
   */
  
   // Monta Objeto JSON de retorno
   oJsonRet := NIL
   oJsonRet := JsonObject():new()

   oJsonRet['Propriedade1'] := EncodeUTF8("Retorno 1", "cp1252") 
   oJsonRet['Propriedade2'] := 10 
   oJsonRet['Propriedade3'] := .T.
  
   // Devolve o retorno para o Rest
   ::SetResponse(oJsonRet:toJSON())
        
   FreeObj(oJsonRet)
   FreeObj(oJson)

Return lRet

/*/-----------------------------------------------------------
{Protheus.doc} PUT
PUT de Exemplo - Método utilizado para Alterações
Uso: WsRstExp
@sample
//PUT / WsRstExp
@author Paulo Henrique Corrêa Cardoso.
@since 21/07/2020
@version 1.0
-------------------------------------------------------------/*/
WSMETHOD PUT WSRECEIVE param1, param2 WSSERVICE WsRstExp
   Local lRet      := .T.         // Recebe o Retorno 
   Local cBody	   := ''          // Recebe o conteudo do Rest
   Local oJson     := NIL         // Recebe o JSON de Entrada
   Local oJsonRet  := NIL         // Recebe o JSON de Saida
   
   // Pega o conteudo JSON da transação Rest
   cBody := ::GetContent()
   ::SetContentType("application/json")

   oJson := JsonObject():new()
   oJson:fromJson(cBody)
  
   /*
   
  Trecho com a sua codificação
   
   */
  
   // Monta Objeto JSON de retorno
   oJsonRet := NIL
   oJsonRet := JsonObject():new()

   oJsonRet['Propriedade1'] := EncodeUTF8("Retorno 1", "cp1252") 
   oJsonRet['Propriedade2'] := 10 
   oJsonRet['Propriedade3'] := .T.
  
   // Devolve o retorno para o Rest
   ::SetResponse(oJsonRet:toJSON())
        
   FreeObj(oJsonRet)
   FreeObj(oJson)

Return lRet

/*/-----------------------------------------------------------
{Protheus.doc} DELETE 
DELETE de Exemplo - Método utilizado para exclusões
Uso: WsRstExp
@sample
//DELETE  / WsRstExp
@author Paulo Henrique Corrêa Cardoso.
@since 21/07/2020
@version 1.0
-------------------------------------------------------------/*/
WSMETHOD DELETE WSRECEIVE param1, param2 WSSERVICE WsRstExp
   Local lRet      := .T.         // Recebe o Retorno 
   Local cBody	   := ''          // Recebe o conteudo do Rest
   Local oJson     := NIL         // Recebe o JSON de Entrada
   Local oJsonRet  := NIL         // Recebe o JSON de Saida
   
   // Pega o conteudo JSON da transação Rest
   cBody := ::GetContent()
   ::SetContentType("application/json")

   oJson := JsonObject():new()
   oJson:fromJson(cBody)
  
   /*
   
  Trecho com a sua codificação
   
   */
  
   // Monta Objeto JSON de retorno
   oJsonRet := NIL
   oJsonRet := JsonObject():new()

   oJsonRet['Propriedade1'] := EncodeUTF8("Retorno 1", "cp1252") 
   oJsonRet['Propriedade2'] := 10 
   oJsonRet['Propriedade3'] := .T.
  
   // Devolve o retorno para o Rest
   ::SetResponse(oJsonRet:toJSON())
        
   FreeObj(oJsonRet)
   FreeObj(oJson)
 
Return lRet
Nos exemplos acima utilizei a Classe JsonObject para tratar dados do tipo JSON, com a mesma é possivel transformar o JSON para objeto e vice-versa.

Muito importante

Para realizar os retornos para o Webservice Rest, temos dois métodos que precisamos utilizar. Em caso de sucesso e vamos devolver um JSON com conteúdo, utilizamos o método ::SetResponse(“[JSON]”). Em caso de falha utilizamos o método SetRestFault([StatusErro]), EncodeUTF8(“[Mensagem de Erro]”, “cp1252”)). Observação : utilizo a função EncodeUTF8(“Texto”, “cp1252”), para suprimir problemas de acentuação ao utilizar o envio de textos pela WS Rest. Nos casos de falha podemos utilizar a tabela abaixo para definir o StatusErro:
Status HTTP Descrição
422 Exceções de negócio
400 Requisição Mal Formada
401 Requisição Requer Autenticação
403 Requisição Negada
404 Recurso não Encontrado
405 Método não Permitido
408 Tempo esgotado para a requisição
413 Requisição excede o tamanho máximo permitido
415 Tipo de mídia inválida (falta de informar o content-type correto, ver JSON)
429 Requisição excede a quantidade máxima de chamadas permitidas à API
500 Erro de servidor
  Vou deixar aqui a documentação da TOTVS com mais algumas informações importantes sobre toda a estrutura do REST. https://tdn.totvs.com/display/framework/REST+ADVPL https://tdn.totvs.com/pages/viewpage.action?pageId=75269436   É isso pessoal. Espero que ajude muito vocês.  

Até a próxima!!!

Sobre o Autor

Paulo Henrique Corrêa Cardoso administrator

Analista de sistemas, formado pela Faculdade de Informática e Administração Paulista . Com mais de 12 anos de experiência em SQL e diversas linguagens de programação. Administrador e desenvolvedor de ERP TOTVS Protheus.

3 Comentários até agora

Wanderson Fernandes de SouzaPostado em10:59 am - abr 20, 2022

Bom dia Paulo,

Gostei muito do seu post e gostaria de sugerir um outro sobre o mesmo assunto porém utilizando um cabeçalho e itens

HiarlyPostado em1:34 pm - abr 27, 2023

Olá Mestre Paulo.

Poderia dar uma exemplo onde tem no seu código…
/*

Trecho com a sua codificação

*/

O que exatamente podemos codificar aqui?
Tipo…
Posso usar …

BeginSQL Alias cAlias

SELECT B1_FILIAL AS FILIAL, B1_COD AS CODIGO,
B1_DESC AS DESCRICAO
FROM SB1010 WHERE D_E_L_E_T_ = ”

EndSQL

/* Por exemplo ?*/

    Paulo Henrique Corrêa CardosoPostado em11:47 am - abr 28, 2023

    Olá Hiarly, tudo bem?
    Poderia sim…
    Por exemplo, se você estiver utilizando o método GET que é o de consulta, pode utilizar uma query de busca no trecho, utilizando os parâmetros de entrada, como por exemplo o param1, param2 como filtro da sua query e retornar o resultado no objeto Json de retorno

    Segue um exemplo com o trecho que você passou:

    WSMETHOD GET WSRECEIVE codProd WSSERVICE WsRstExp
    Local lRet := .T. // Recebe o Retorno
    Local oJsonRet := NIL // Recebe o JSON de Saida

    BeginSQL Alias cAlias

    SELECT B1_FILIAL AS FILIAL, B1_COD AS CODIGO,
    B1_DESC AS DESCRICAO
    FROM %table:SB1%
    WHERE D_E_L_E_T_ = ‘ ‘
    AND B1_COD = %exp:codProd%

    EndSQL

    // Monta Objeto JSON de retorno
    oJsonRet := NIL
    oJsonRet := JsonObject():new()

    If !(cAlias)->(Eof())
    oJsonRet[‘Filial’] := EncodeUTF8((cAlias)->FILIAL, “cp1252”)
    oJsonRet[‘Codigo’] := EncodeUTF8((cAlias)->CODIGO, “cp1252”)
    oJsonRet[‘Descricao’] := EncodeUTF8((cAlias)->DESCRICAO, “cp1252”)
    Self:SetStatus(200)
    Else
    Self:SetStatus(400)
    oJsonRet[‘Mensagem’] := EncodeUTF8(“Dados não encontrados”, “cp1252”)
    EndIf

    // Devolve o retorno para o Rest
    Self:SetResponse(oJsonRet:toJSON())

    Return lRet

Deixe uma resposta

%d blogueiros gostam disto: