Trabalhando com Webservice Rest ADVPL

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: