restclient — n_pbt_restclient #
← Referência dos componentes · Índice do guia
Um cliente HTTPS para PowerBuilder, versão 10 incluída: GET, POST, PUT, PATCH, DELETE, cabeçalhos, um corpo JSON, a transferência de um ficheiro. Nativo — o TLS, o proxy e a descompressão do sistema, e nenhum muro CORS, porque o pedido não parte de uma página.
▶ Ver ao vivo — Aplicação de demonstração, mosaico REST client: as respostas, o código que as obtém e esta página, lado a lado (ligação à Internet necessária).
Em resumo #
| Objeto não visual | n_pbt_restclient |
| Serve para | Chamar uma API REST a partir de uma aplicação PowerBuilder: ler, criar, atualizar, transferir |
| Princípio | Um pedido é uma chamada que devolve o estado HTTP; a resposta espera no objeto, lida por of_response_text ou of_json_value |
| Dependência | WinHTTP, na DLL da biblioteca — sem página, sem runtime adicional |
Início rápido #
n_pbt_restclient lnv_rest
n_pbt_json lnv_j
long ll_status
lnv_rest = create n_pbt_restclient
lnv_rest.is_base_url = "https://api.example.com"
lnv_rest.of_set_bearer(ls_token)
// Read : the status comes back, the body waits in the object
ll_status = lnv_rest.of_get("/orders/4152")
if ll_status = 200 then ls_customer = lnv_rest.of_json_value("customer")
// Create : the body is built with n_pbt_json, never by hand
lnv_j.of_set_number("order", 4152)
lnv_j.of_set_string("status", "shipped")
ll_status = lnv_rest.of_post("/shipments", lnv_j.of_text())
if ll_status < 0 then MessageBox("API", lnv_rest.is_last_error)
destroy lnv_rest
Cada método HTTP devolve o estado que o servidor escolheu — um 404 é uma resposta, não um erro — ou um código negativo quando o pedido não passou: -5 URL inválido, -4 falha (sem rede, nome desconhecido, tempo esgotado, TLS; is_last_error diz qual), -6 recusado em modo demo.
Porquê nativo, e não a página #
Um fetch() lançado a partir de uma página da biblioteca está sujeito a CORS: toda a API que não envie Access-Control-Allow-Origin fica-lhe fechada, e as API de empresa não o enviam. O pedido parte por isso da DLL, por WinHTTP: o TLS do sistema com os seus certificados, o proxy configurado, gzip. O que perde: nada; o que ganha: todas as API.
Propriedades #
| Propriedade | Tipo | Predefinição | Função |
|---|---|---|---|
is_base_url | string | "" | Posto à frente de um URL relativo: is_base_url = "https://api.example.com" e depois of_get("/orders/4152"). Um URL absoluto é usado tal como está |
il_timeout_ms | long | 30000 | Duração máxima de um pedido, ligação e resposta incluídas. Depois: -4 e «timed out» |
is_last_error | string | "" | Porque o último pedido devolveu um código negativo. Vazio após um pedido que chegou ao servidor — um 404 não é um erro |
il_status | long | 0 | O estado HTTP do último pedido, ou o seu código negativo; o valor que o pedido devolveu |
Constantes: METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_PATCH, METHOD_DELETE para of_request.
Métodos #
| Método | Função |
|---|---|
of_set_header (string as_name, string as_value) | Um cabeçalho enviado com cada pedido a partir de agora; um nome já definido é substituído. Content-Type é acrescentado sozinho quando um corpo parte sem ele (JSON, UTF-8) |
of_remove_header (string as_name) | Esquece um cabeçalho, por nome (sem distinção de maiúsculas) |
of_clear_headers ( ) | Esquece todos os cabeçalhos, autenticação incluída |
of_set_bearer (string as_token) | Authorization: Bearer em cada pedido; um token vazio remove-o |
of_set_basic (string as_user, string as_password) | Autenticação Basic; a DLL codifica ela própria o base64 — o PowerBuilder 10 não o tem |
of_get (string as_url) → long | GET. Devolve o estado HTTP, ou -5 URL inválido, -4 falha, -6 recusado em demo |
of_post (string as_url, string as_body) → long | POST com um corpo (JSON por defeito, construído com n_pbt_json). Devolve o estado HTTP, ou -5 / -4 / -6 |
of_put (string as_url, string as_body) → long | PUT com um corpo. Devolve o estado HTTP, ou -5 / -4 / -6 |
of_patch (string as_url, string as_body) → long | PATCH com um corpo. Devolve o estado HTTP, ou -5 / -4 / -6 |
of_delete (string as_url) → long | DELETE. Devolve o estado HTTP, ou -5 / -4 / -6 |
of_request (string as_method, string as_url, string as_body) → long | Qualquer método (METHOD_*, ou o seu), qualquer corpo enviado em UTF-8: o que os cinco atalhos chamam. Devolve o estado HTTP, ou -5 / -4 / -6 |
of_download (string as_url, string as_path) → long | GET diretamente para um ficheiro, bytes intactos, qualquer que seja o tamanho. Devolve o estado HTTP, ou -5 URL ou caminho vazio, -4 falha, -6 recusado em demo |
of_response_text ( ) → string | O corpo da última resposta, em texto (UTF-8 descodificado) |
of_response_headers ( ) → string | Todos os cabeçalhos da última resposta, um por linha |
of_response_header (string as_name) → string | Um cabeçalho da última resposta, por nome; vazio se não foi enviado |
of_json_value (string as_key) → string | Um valor do corpo JSON pela sua chave, a primeira ocorrência a qualquer profundidade. Para mais do que um valor, leia of_response_text e n_pbt_utils.of_json_get_* |
of_reset ( ) | Regresso aos valores por defeito: sem cabeçalhos, sem URL de base, o tempo por defeito, a última resposta esquecida |
Exemplos #
Ler uma lista e percorrê-la #
string ls_body
if lnv_rest.of_get("/orders?status=open") = 200 then
ls_body = lnv_rest.of_response_text()
// One value : of_json_value. The whole list : n_pbt_utils.of_json_get_payload and
// of_json_get_str on each object, or your own parser on ls_body.
end if
Transferir um documento #
long ll_status
ll_status = lnv_rest.of_download("/orders/4152/invoice.pdf", "C:\temp\invoice-4152.pdf")
if ll_status = 200 then
Run("C:\temp\invoice-4152.pdf")
elseif ll_status < 0 then
MessageBox("Download", lnv_rest.is_last_error)
end if
Uma API que responde com erro #
choose case lnv_rest.of_post("/shipments", ls_body)
case 200, 201
// created
case 401
MessageBox("API", "Token expired : " + lnv_rest.of_json_value("message"))
case is < 0
MessageBox("Network", lnv_rest.is_last_error)
end choose
Assíncrono (sem congelar a interface) #
As chamadas síncronas acima bloqueiam o script (a interface fica viva mas a linha espera). A API assíncrona nem sequer bloqueia o script: o pedido corre num fio de trabalho e a resposta volta mais tarde como evento.
Preparação — nada a ligar: o componente entrega a resposta sozinho em ue_response. of_open / of_close abrem e fecham o cliente (facultativo: o primeiro pedido async abre-o), of_clear_cookies esquece os cookies guardados entre pedidos. Internamente o pump do componente chama of_process_events para distribuir as respostas — nunca o chama.
Enviar — of_request_async(method, url, body), ou os atalhos of_get_async, of_post_async, of_put_async, of_patch_async, of_delete_async, devolvem de imediato um id de pedido. of_download_async(url, path) descarrega para um ficheiro e of_upload(url, field, path) envia um ficheiro como multipart/form-data — ambos com progresso. of_cancel(id) para um pedido em curso. As tentativas (il_max_retries, il_retry_backoff_ms) repetem em 429/503/falha de rede.
Receber — ue_response(al_id, al_status) (ler o corpo com of_response_text(al_id) / of_status(al_id) DENTRO do tratador), ue_failed(al_id, as_error), ue_progress(al_id, al_done, al_total).
| Método | Função |
|---|---|
of_open ( ) → long | Abre o cliente assíncrono, um canal de eventos nativo (sem WebView, sem CORS). Facultativo: o primeiro pedido async abre-o. Devolve o id do cliente (> 0), ou um código negativo |
of_close ( ) | Fecha o cliente assíncrono: os seus cookies (a sessão) vão com ele. O próximo pedido async reabre-o. Feito por si por of_reset e na destruição |
of_process_events ( ) | Distribui as respostas assíncronas e levanta ue_response / ue_failed / ue_progress. Pública APENAS porque o pump interno do componente a chama no ciclo PowerBuilder, de poucos em poucos milissegundos enquanto um pedido está em curso — nunca a chama, e não liga nem recetor nem temporizador. Chamá-la você mesmo é inofensivo: só esvazia o que já está à espera |
of_clear_cookies ( ) | Esquece os cookies guardados entre pedidos (um logout). A definição ib_keep_cookies não muda |
of_get_async (string as_url) → long | GET assíncrono. Devolve de imediato um id de pedido; a resposta chega em ue_response(id, status). Devolve -5 num URL inválido, -2 se o cliente não conseguir abrir |
of_post_async (string as_url, string as_body) → long | POST assíncrono com um corpo (JSON por defeito, construído com n_pbt_json). Devolve de imediato um id de pedido; a resposta chega em ue_response. Devolve -5 / -2 como of_get_async |
of_put_async (string as_url, string as_body) → long | PUT assíncrono com um corpo. Devolve de imediato um id de pedido; a resposta chega em ue_response. Devolve -5 / -2 como of_get_async |
of_patch_async (string as_url, string as_body) → long | PATCH assíncrono com um corpo. Devolve de imediato um id de pedido; a resposta chega em ue_response. Devolve -5 / -2 como of_get_async |
of_delete_async (string as_url) → long | DELETE assíncrono. Devolve de imediato um id de pedido; a resposta chega em ue_response. Devolve -5 / -2 como of_get_async |
of_request_async (string as_method, string as_url, string as_body) → long | Pedido assíncrono com o método HTTP à sua escolha (of_get_async e os seus irmãos chamam-no). Devolve de imediato um id de pedido; a resposta chega em ue_response, uma falha em ue_failed. Devolve -2 se o cliente não abrir |
of_download_async (string as_url, string as_path) → long | GET assíncrono diretamente para um FICHEIRO, com ue_progress pelo caminho. Devolve um id de pedido, -5 com um caminho vazio, -2 se o cliente não abrir |
of_upload (string as_url, string as_field, string as_path) → long | Envia um FICHEIRO como multipart/form-data (campo de formulário as_field), de forma assíncrona, com ue_progress. Devolve um id de pedido, -5 com um caminho vazio, -2 se o cliente não abrir, -6 em modo demo |
of_cancel (long al_id) → long | Pede a um pedido em curso que pare; termina com ue_failed(id, "cancelled"). Devolve 0, ou -1 se o id for desconhecido |
of_status (long al_id) → long | O estado HTTP de uma resposta assíncrona, por id de pedido - a ler DENTRO de ue_response. Devolve o estado (200, 404...), 0 se o pedido for desconhecido ou já esquecido |
| Evento | Função |
|---|---|
ue_response (long al_id, long al_status) | Um pedido assíncrono terminou: al_id é o id devolvido no envio, al_status o estado HTTP. Leia o corpo com of_response_text(al_id) DENTRO do handler: o pedido é esquecido à saída do evento |
ue_failed (long al_id, string as_error) | Um pedido assíncrono falhou: sem rede, tempo esgotado, TLS, ou cancelado por of_cancel (as_error vale então cancelled) |
ue_progress (long al_id, long al_done, long al_total) | Progresso de uma transferência (of_download_async) ou envio (of_upload) assíncrono: al_done bytes de al_total |
// Async : the UI never freezes, and there is nothing to wire.
ll_id = uo_rest.of_get_async("https://api.example.com/orders/4152")
// The answer arrives on its own in the ue_response event of uo_rest :
IF al_status >= 200 AND al_status < 300 THEN
ls_body = uo_rest.of_response_text(al_id)
END IF
Boas práticas #
- Um cliente por API, com o seu
is_base_urle os seus cabeçalhos definidos uma vez: o código das chamadas só transporta o caminho e o corpo. - O corpo constrói-se com
n_pbt_json, nunca por concatenação: uma aspa num nome de cliente não parte nada. - Teste o estado, depois o código negativo:
>= 200 e < 300está bem,>= 400o servidor disse não,< 0o pedido não partiu —is_last_errordiz porquê. - O tempo limite: 30 segundos por defeito; um relatório pesado merece mais, uma verificação de presença menos.