Fazendo requests POST/GET. (PowerBuilder)

Ha algum tempo precisei utilizar o PowerBuilder para realizar requisições GET e POST. Como não foi algo tão trivial quanto deveria e como as informações estavam todas picadas, resolvi fazer este post para ajudar no entendimento.

Antes de começarmos, um aviso: Vou passar os exemplos de códigos aqui, mas criei um repositório com objetos que vão te auxiliar neste processo.

 

Passo 1: Tratando conteúdo das respostas

A primeira coisa que você tem que saber é que precisa criar um objeto seu, herdado de internetresult. Nele você precisa criar o código para a função internetdata. Ela é uma função de callback, que é chamada automaticamente pelo PB, assim que a sua request é concluída. Não existe um padrão para tratar este resultado e o código é bem simples: Basta converter uma variável blob para string.

Vamos chamar este objeto que decodifica o resultado de uo_inet_result. Para cria-lo, primeiro acesse o menu File, New, aba PB Object, Standard Class e selecione a opção internetresult.

Agora abra o painter deste objeto que você acabou de criar e clique na aba “Declare Instance Variables”. Nela, inclua o seguinte código:

protectedwrite string is_result

Desta forma, você cria uma variável que será acessível por outros objetos, mas que não pode ser modificada externamente. Isso é o suficiente para este exemplo.

 

Agora abra o fonte da função internetdata e insira o seguinte código:

this.is_result = string(data, EncodingUTF8!)

Como disse anteriormente, o procedimento é simples. Basta converter o conteúdo do argumento ‘data‘ de blob para string. Para trabalhar de forma mais segura, coloco o enum EncodingUTF8!. Desta forma, garantimos (ou algo bem próximo a isso) que não vão aparecer caracteres estranhos.

 

Passo 2: Tratando código de retorno

Agora a parte mais difícil está feita. Antes de mostrar como fazer as requests get e post, uma pequena (mas importante) nota: Esta forma de realizar requisições não te da acesso ao status_code da requisição. Você deve tratar os códigos de retorno específicos dos objeto. Veja abaixo:

Código de retorno Tipo da requisição Descrição
1 GET / POST Sucesso!
-1 GET / POST Erro genérico. Aqui pode ser quase qualquer coisa, incluindo falha na criação do objeto herdado de internetresult (que fizemos no passo anterior).
-2 GET / POST URL inválida
-4 GET / POST Não foi possível se conectar a internet
-5 POST Requisição feita utilizando protocolo não suportado. (O PB não da suporte para requisições post, usando https.)
-6 POST Requisição falhou.

 

(Aposto que você não percebeu, mas não existe um código de retorno -3.)

 

Passo 3: Fazendo requisição GET

Abaixo está o exemplo de uma requisição GET simples. Ela utiliza o objeto que criamos no passo 1.

int li_get_ret
string ls_url
uo_inet_result luo_get_result
inet l_inet

ls_url = "https://raccoon-ninja-dummy-api.herokuapp.com/api/v1/ping"
l_inet = create inet

li_get_ret = i_inet.getURL(ls_url, luo_get_result)
if li_get_ret = 1 then
  MessageBox("Success!", luo_get_result.is_result, Information!)
else
  MessageBox("Error!", "Deu ruim!", StopSign!)
end if

No código acima:

  1. primeiro declaramos e definimos as variáveis.
  2. na sequencia, utilizo o objeto inet (l_inet) para fazer a requisição GET).
  3. depois verifico o código de retorno (li_get_ret), se tudo deu certo (= 1), mostro um MessageBox com o resultado. Se deu errado, mostro outra MessageBox com uma mensagem de erro.

 

Passo 4: Fazendo uma requisição POST

Este tipo de post é bem parecido, com poucas diferenças:

  1. Temos que definir um header para enviar com a requisição;
  2. Precisamos converter o conteúdo que será enviado para blob;
int li_post_ret
string ls_url
string ls_data
string ls_header
long ll_port
blob lblb_data
uo_inet_result luo_get_result
inet l_inet

l_inet = create inet
ls_url = "https://raccoon-ninja-dummy-api.herokuapp.com/api/v1/echo"
ls_data = "The quick brown fox jumps over the lazy dog."
ls_headers = "Content-Length: " + string(len(ls_data))
ll_port = 80
lblb_data = blob(ls_data, EncodingUTF8!)

Nó código acima:

  1. Coloquei o conteúdo que desejo enviar via POST na variável ls_data
  2. Criei a variável ls_headers para armazenar a informação de header. Nela coloquei apenas o tamanho dos dados que estão sendo enviados;
  3. Na variável ll_port, configurei a porta que vai ser utilizada.
  4. Por último, converti a variável ls_data de string para blob.

 

Agora que isso foi resolvido, basta fazer a requisição em si:

li_post_ret = l_inet.postURL(ls_url, lblb_data, ls_header, ll_port, luo_post_result)

if li_post_ret = 1 then
  MessageBox("Success!", luo_get_result.is_result, Information!)
else
  MessageBox("Error!", "Deu ruim!", StopSign!)
end if

Acima, apenas utilizei a função PostURL do objeto l_inet e processei o retorno dele, da mesma forma que fiz com a requisição GET.

 

Atenção aos detalhes!

Então, o PowerBuilder possui alguns comportamentos que você deve sempre ter em mente:

  1. Nas requisições GET, para mudar a porta utilizada, você tem que colocar esta informação na URL. (exemplo: http://localhost:5000 ou http://localhost:8080);
  2. Já nas requisições POST, não adianta colocar a porta na URL, você deve utilizar o parâmetro fornecido no método. O PB vai ignorar a porta da URL;
  3. Atenção: Não é possível fazer requisições POST usando HTTPS. GET é tranquilo;
  4. Requisições post não podem ter querystring.

 

Exemplos no Github

Criei uma aplicação de demonstração. Nela inclui uma PBL com os objetos já prontos para serem utilizados (uo_requester e uo_inet_result).

Para utiliza-los, veja os exemplos:

 

Request GET com Querystring #1

//Declaring variables
string ls_method, ls_querystring, ls_url
string ls_result
uo_requester luo_requester

//Initializing
ls_method = "echoargs"
ls_querystring = "?foo=42&bar=code"
luo_requester = create uo_requester

//Setting the base url for all requests.
luo_requester.of_set_base_url("https://raccoon-ninja-dummy-api.herokuapp.com/api/v1/")

//Getting the full URL 
ls_url = luo_requester.of_get_full_url(ls_method + ls_querystring)

//The actual GET request.
ls_result = luo_requester.of_get(ls_url)

//Testing result.
if isNull(ls_result) then
	//Something went wrong.
	
else
	//ls_result contains the returned value of the request.
	
end if

//Destroying object.
destroy luo_requester

 

Request GET com Querystring #2

//Declaring variables
string ls_url
string ls_result
uo_requester luo_requester

//Initializing
luo_requester = create uo_requester

//Defining the full URL 
ls_url = "https://raccoon-ninja-dummy-api.herokuapp.com/api/v1/echoargs?foo=42&bar=code"

//The actual GET request.
ls_result = luo_requester.of_get(ls_url)

//Testing result.
if isNull(ls_result) then
	//Something went wrong.
	
else
	//ls_result contains the returned value of the request.
	
end if

//Destroying object.
destroy luo_requester

 

Request POST

//Declaring variables
string ls_headers
string ls_url
string ls_data
string ls_result
blob lblb_data
long ll_data_size
uo_requester luo_requester

//Initializing
luo_requester = create uo_requester

//Defining the full URL 
ls_url = "https://raccoon-ninja-dummy-api.herokuapp.com/api/v1/echo"

//Preparing data that will be sent
ls_data = "I'll send this to the webservice!"

if luo_requester.of_encode_args(ls_data, lblb_data, ll_data_size) = -1 then
	//Error encoding data.
	return -1
end if

//Getting headers.
ls_headers = luo_requester.of_get_headers(ll_data_size)

//The actual GET request.
ls_result = luo_requester.of_post(ls_url, lblb_data, ls_headers)

//Testing result.
if isNull(ls_result) then
	//Something went wrong.
	
else
	//ls_result contains the returned value of the request.
	
end if

//Destroying object.
destroy luo_requester

 

 

Como encapsular estes métodos

Para facilitar, criei um objeto chamado uo_dummy_requester, que encapsula as chamadas para uma api de teste que criei. Você pode utiliza-la como exemplo para encapsular as chamadas que você precisa utilizar.

 

Link para o fonte no GitHubhttps://github.com/brenordv/powerbuilder_webservice_poc

 

 

Espero ter ajudado!

 

Referências:

  1. inet.GetURL
  2. inet.PostURL
The following two tabs change content below.
Arquiteto de Software e Desenvolvedor Backend (quase Fullstack), geralmente trabalho com C#, PowerShell, Python, Golang, bash e Unity (esse é mais por hobby). Estou sempre buscando algo novo para aprender, adicionando novas ferramentas ao meu cinto de utilidades.
Posted in Dev, PowerBuilder and tagged , , , , , .