restclient — n_pbt_restclient #
← Referencia de componentes · Índice de la guía
Un cliente HTTPS para PowerBuilder, versión 10 incluida: GET, POST, PUT, PATCH, DELETE, cabeceras, un cuerpo JSON, la descarga de un archivo. Nativo — el TLS, el proxy y la descompresión del sistema, y ningún muro CORS, porque la petición no sale de una página.
▶ Verlo en vivo — Aplicación de demostración, mosaico REST client: las respuestas, el código que las obtiene y esta página, lado a lado (se requiere conexión a Internet).
En resumen #
| Objeto no visual | n_pbt_restclient |
| Sirve para | Llamar a una API REST desde una aplicación PowerBuilder: leer, crear, actualizar, descargar |
| Principio | Una petición es una llamada que devuelve el estado HTTP; la respuesta espera en el objeto, leída con of_response_text u of_json_value |
| Dependencia | WinHTTP, en la DLL de la biblioteca — sin página, sin runtime adicional |
Inicio 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 devuelve el estado que eligió el servidor — un 404 es una respuesta, no un error — o un código negativo cuando la petición no llegó: -5 URL no válida, -4 fallo (sin red, nombre desconocido, tiempo agotado, TLS; is_last_error dice cuál), -6 rechazado en modo demo.
Por qué nativo, y no la página #
Un fetch() lanzado desde una página de la biblioteca está sujeto a CORS: toda API que no envíe Access-Control-Allow-Origin le queda cerrada, y las API de empresa no lo envían. La petición sale por tanto de la DLL, por WinHTTP: el TLS del sistema con sus certificados, el proxy configurado, gzip. Lo que pierde: nada; lo que gana: todas las API.
Propiedades #
| Propiedad | Tipo | Predeterminado | Función |
|---|---|---|---|
is_base_url | string | "" | Puesto delante de una URL relativa: is_base_url = "https://api.example.com" y luego of_get("/orders/4152"). Una URL absoluta se usa tal cual |
il_timeout_ms | long | 30000 | Duración máxima de una petición, conexión y respuesta incluidas. Pasado: -4 y «timed out» |
is_last_error | string | "" | Por qué la última petición devolvió un código negativo. Vacío tras una petición que llegó al servidor — un 404 no es un error |
il_status | long | 0 | El estado HTTP de la última petición, o su código negativo; el valor que devolvió la petición |
Constantes: METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_PATCH, METHOD_DELETE para of_request.
Métodos #
| Método | Función |
|---|---|
of_set_header (string as_name, string as_value) | Una cabecera enviada con cada petición desde ahora; un nombre ya puesto se reemplaza. Content-Type se añade solo cuando un cuerpo sale sin él (JSON, UTF-8) |
of_remove_header (string as_name) | Olvida una cabecera, por nombre (sin distinguir mayúsculas) |
of_clear_headers ( ) | Olvida todas las cabeceras, la autenticación incluida |
of_set_bearer (string as_token) | Authorization: Bearer en cada petición; un token vacío lo quita |
of_set_basic (string as_user, string as_password) | Autenticación Basic; la DLL codifica el base64 por sí misma — PowerBuilder 10 no lo tiene |
of_get (string as_url) → long | GET. Devuelve el estado HTTP, o -5 URL no válida, -4 fallo, -6 rechazado en demo |
of_post (string as_url, string as_body) → long | POST con un cuerpo (JSON por defecto, construido con n_pbt_json). Devuelve el estado HTTP, o -5 / -4 / -6 |
of_put (string as_url, string as_body) → long | PUT con un cuerpo. Devuelve el estado HTTP, o -5 / -4 / -6 |
of_patch (string as_url, string as_body) → long | PATCH con un cuerpo. Devuelve el estado HTTP, o -5 / -4 / -6 |
of_delete (string as_url) → long | DELETE. Devuelve el estado HTTP, o -5 / -4 / -6 |
of_request (string as_method, string as_url, string as_body) → long | Cualquier método (METHOD_*, o el suyo), cualquier cuerpo enviado en UTF-8: lo que llaman los cinco atajos. Devuelve el estado HTTP, o -5 / -4 / -6 |
of_download (string as_url, string as_path) → long | GET directamente a un archivo, bytes intactos, sea cual sea el tamaño. Devuelve el estado HTTP, o -5 URL o ruta vacía, -4 fallo, -6 rechazado en demo |
of_response_text ( ) → string | El cuerpo de la última respuesta, como texto (UTF-8 decodificado) |
of_response_headers ( ) → string | Todas las cabeceras de la última respuesta, una por línea |
of_response_header (string as_name) → string | Una cabecera de la última respuesta, por nombre; vacía si no se envió |
of_json_value (string as_key) → string | Un valor del cuerpo JSON por su clave, la primera aparición a cualquier profundidad. Para más de un valor, lea of_response_text y n_pbt_utils.of_json_get_* |
of_reset ( ) | Vuelta a los valores por defecto: sin cabeceras, sin URL base, el tiempo por defecto, la última respuesta olvidada |
Ejemplos #
Leer una lista y recorrerla #
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
Descargar un 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
Una API que responde con error #
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
Asíncrono (sin congelar la interfaz) #
Las llamadas síncronas anteriores bloquean el script (la interfaz sigue viva pero la línea espera). La API asíncrona ni siquiera bloquea el script: la petición corre en un hilo de trabajo y su respuesta vuelve más tarde como evento.
Preparación — nada que cablear: el componente entrega la respuesta por sí solo en ue_response. of_open / of_close abren y cierran el cliente (opcional: la primera llamada asíncrona lo abre), of_clear_cookies olvida las cookies guardadas entre peticiones. Internamente el pump del componente llama a of_process_events para repartir las respuestas — usted nunca lo llama.
Enviar — of_request_async(method, url, body), o los atajos of_get_async, of_post_async, of_put_async, of_patch_async, of_delete_async, devuelven de inmediato un id de petición. of_download_async(url, path) descarga a un archivo y of_upload(url, field, path) sube un archivo como multipart/form-data — ambos con progreso. of_cancel(id) detiene una petición en vuelo. Los reintentos (il_max_retries, il_retry_backoff_ms) reintentan en 429/503/fallo de red.
Recibir — ue_response(al_id, al_status) (leer el cuerpo con of_response_text(al_id) / of_status(al_id) DENTRO del manejador), ue_failed(al_id, as_error), ue_progress(al_id, al_done, al_total).
| Método | Función |
|---|---|
of_open ( ) → long | Abre el cliente asíncrono, un canal de eventos nativo (sin WebView, sin CORS). Opcional: la primera llamada asíncrona lo abre. Devuelve el id del cliente (> 0), o un código negativo |
of_close ( ) | Cierra el cliente asíncrono: sus cookies (la sesión) se van con él. La siguiente llamada asíncrona lo reabre. Hecho por usted por of_reset y al destruir |
of_process_events ( ) | Reparte las respuestas asíncronas y lanza ue_response / ue_failed / ue_progress. Pública SOLO porque el pump interno del componente la llama en el bucle de PowerBuilder, cada pocos milisegundos mientras una petición está en vuelo — usted nunca la llama, y no cablea ni receptor ni temporizador. Llamarla usted mismo es inofensivo: solo vacía lo que ya está esperando |
of_clear_cookies ( ) | Olvida las cookies guardadas entre peticiones (un cierre de sesión). El ajuste ib_keep_cookies no cambia |
of_get_async (string as_url) → long | GET asíncrono. Devuelve de inmediato un id de petición; la respuesta llega en ue_response(id, status). Devuelve -5 con una URL no válida, -2 si el cliente no puede abrirse |
of_post_async (string as_url, string as_body) → long | POST asíncrono con un cuerpo (JSON por defecto, construido con n_pbt_json). Devuelve de inmediato un id de petición; la respuesta llega en ue_response. Devuelve -5 / -2 como of_get_async |
of_put_async (string as_url, string as_body) → long | PUT asíncrono con un cuerpo. Devuelve de inmediato un id de petición; la respuesta llega en ue_response. Devuelve -5 / -2 como of_get_async |
of_patch_async (string as_url, string as_body) → long | PATCH asíncrono con un cuerpo. Devuelve de inmediato un id de petición; la respuesta llega en ue_response. Devuelve -5 / -2 como of_get_async |
of_delete_async (string as_url) → long | DELETE asíncrono. Devuelve de inmediato un id de petición; la respuesta llega en ue_response. Devuelve -5 / -2 como of_get_async |
of_request_async (string as_method, string as_url, string as_body) → long | Petición asíncrona con el método HTTP que elija (of_get_async y sus hermanos la llaman). Devuelve de inmediato un id de petición; la respuesta llega en ue_response, un fallo en ue_failed. Devuelve -2 si el cliente no se abre |
of_download_async (string as_url, string as_path) → long | GET asíncrono directamente a un ARCHIVO, con ue_progress por el camino. Devuelve un id de petición, -5 con una ruta vacía, -2 si el cliente no se abre |
of_upload (string as_url, string as_field, string as_path) → long | Sube un ARCHIVO como multipart/form-data (campo de formulario as_field), de forma asíncrona, con ue_progress. Devuelve un id de petición, -5 con una ruta vacía, -2 si el cliente no se abre, -6 en modo demo |
of_cancel (long al_id) → long | Pide a una petición en curso que se detenga; termina con ue_failed(id, "cancelled"). Devuelve 0, o -1 si el id es desconocido |
of_status (long al_id) → long | El estado HTTP de una respuesta asíncrona, por id de petición - léalo DENTRO de ue_response. Devuelve el estado (200, 404...), 0 si la petición es desconocida o ya olvidada |
| Evento | Función |
|---|---|
ue_response (long al_id, long al_status) | Una petición asíncrona ha terminado: al_id es el id devuelto al enviar, al_status el estado HTTP. Lea el cuerpo con of_response_text(al_id) DENTRO del manejador: la petición se olvida al salir del evento |
ue_failed (long al_id, string as_error) | Una petición asíncrona ha fallado: sin red, tiempo agotado, TLS, o cancelada por of_cancel (as_error vale entonces cancelled) |
ue_progress (long al_id, long al_done, long al_total) | Progreso de una descarga (of_download_async) o subida (of_upload) asíncrona: 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
Buenas prácticas #
- Un cliente por API, con su
is_base_urly sus cabeceras puestas una vez: el código de las llamadas solo lleva la ruta y el cuerpo. - El cuerpo se construye con
n_pbt_json, nunca por concatenación: una comilla en el nombre de un cliente no rompe nada. - Compruebe el estado, luego el código negativo:
>= 200 y < 300está bien,>= 400el servidor dijo no,< 0la petición no salió —is_last_errordice por qué. - El tiempo de espera: 30 segundos por defecto; un informe pesado merece más, una comprobación de presencia menos.