Tutorial: Construir una Aplicacion Web
HarbourBuilder incluye un componente TWebServer integrado que le permite servir paginas web y APIs JSON directamente desde su aplicacion. En este tutorial construira una aplicacion web TODO simple con una interfaz de escritorio GUI y una interfaz basada en navegador funcionando lado a lado.
Paso 1: Crear el Proyecto
- Cree un nuevo proyecto llamado
TodoWebAppmediante Archivo → Nuevo Proyecto. - Abra
main.prgen el Editor de Codigo. - Construiremos un formulario de escritorio para gestionar tareas y un servidor web que expone los mismos datos.
Paso 2: Configurar el Servidor Web
Coloque un TWebServer desde la pestania de paleta Internet en el formulario, o creelo en codigo. Establezca el puerto y defina sus rutas.
#include "hbbuilder.ch" static aTodos := {} // Lista de tareas compartida function Main() local oForm, oServer, oBrw, oGet, oBtnAdd local cTask := "" // --- Configuracion del servidor web --- DEFINE WEBSERVER oServer PORT 8080 oServer:Route( "GET", "/", { |oReq, oRes| ServeHomePage( oReq, oRes ) } ) oServer:Route( "GET", "/api/todos", { |oReq, oRes| GetTodosJSON( oReq, oRes ) } ) oServer:Route( "POST", "/api/todos", { |oReq, oRes| AddTodoJSON( oReq, oRes ) } ) oServer:Route( "DELETE", "/api/todos/:id", { |oReq, oRes| DeleteTodoJSON( oReq, oRes ) } ) oServer:Start() // --- Interfaz de escritorio --- DEFINE FORM oForm TITLE "Aplicacion TODO (Escritorio + Web en puerto 8080)" ; SIZE 600, 500 FONT "Segoe UI", 10 @ 10, 10 BROWSE oBrw ; OF oForm SIZE 560, 350 ; HEADERS { "#", "Tarea", "Estado" } ; WIDTHS { 40, 350, 100 } @ 380, 10 GET oGet VAR cTask OF oForm SIZE 400, 24 @ 380, 420 BUTTON oBtnAdd PROMPT "Anadir Tarea" ; OF oForm SIZE 100, 32 ; ACTION ( AAdd( aTodos, { oGet:GetValue(), "pendiente" } ), ; RefreshGrid( oBrw ), oGet:SetValue( "" ) ) RefreshGrid( oBrw ) oForm:OnClose := { || oServer:Stop(), .T. } ACTIVATE FORM oForm CENTERED return nil
El TWebServer se ejecuta en un hilo en segundo plano, por lo que su formulario de escritorio permanece completamente interactivo. Tanto la GUI como el navegador ven el mismo arreglo aTodos. Los cambios de cualquier lado son inmediatamente visibles para el otro.
Paso 3: Servir una Pagina HTML
La ruta raiz / sirve una pagina HTML completa. Use oRes:SendHTML() para devolver contenido HTML.
static function ServeHomePage( oReq, oRes ) local cHTML := '<!DOCTYPE html>' + ; '<html><head><title>Aplicacion TODO</title>' + ; '<style>body{font-family:sans-serif;max-width:600px;margin:2em auto}' + ; 'input{padding:8px;width:70%} button{padding:8px 16px}' + ; 'li{padding:4px 0}</style></head><body>' + ; '<h1>Lista de Tareas</h1>' + ; '<input id="task" placeholder="Nueva tarea...">' + ; '<button onclick="addTask()">Anadir</button>' + ; '<ul id="list"></ul>' + ; '<script>' + ; 'async function load(){let r=await fetch("/api/todos");' + ; 'let d=await r.json();let h="";' + ; 'd.forEach((t,i)=>h+="<li>"+t[0]+" ["+t[1]+"] " + ; '+"<a href=# onclick=del("+i+")>x</a></li>");' + ; 'document.getElementById("list").innerHTML=h}' + ; 'async function addTask(){let v=document.getElementById("task").value;' + ; 'await fetch("/api/todos",{method:"POST",' + ; 'headers:{"Content-Type":"application/json"},' + ; 'body:JSON.stringify({task:v})});load()}' + ; 'async function del(i){await fetch("/api/todos/"+i,' + ; '{method:"DELETE"});load()}' + ; 'load();</script></body></html>' oRes:SendHTML( cHTML ) return nil
Paso 4: Devolver JSON para la API
Las rutas de API usan oRes:SendJSON() para devolver datos estructurados que el JavaScript del navegador (o cualquier cliente HTTP) puede consumir.
static function GetTodosJSON( oReq, oRes ) oRes:SendJSON( hb_jsonEncode( aTodos ) ) return nil static function AddTodoJSON( oReq, oRes ) local hBody := hb_jsonDecode( oReq:cBody ) AAdd( aTodos, { hBody[ "task" ], "pendiente" } ) oRes:SendJSON( '{"status":"ok"}' ) return nil static function DeleteTodoJSON( oReq, oRes ) local nId := Val( oReq:Param( "id" ) ) + 1 if nId >= 1 .and. nId <= Len( aTodos ) ADel( aTodos, nId ) ASize( aTodos, Len( aTodos ) - 1 ) endif oRes:SendJSON( '{"status":"ok"}' ) return nil
Paso 5: Actualizar la Cuadricula de Escritorio
static function RefreshGrid( oBrw ) local aData := {}, n for n := 1 to Len( aTodos ) AAdd( aData, { n, aTodos[ n ][ 1 ], aTodos[ n ][ 2 ] } ) next oBrw:SetArray( aData ) oBrw:Refresh() return nil
Paso 6: Compilar y Probar
- Presione F9 para compilar y ejecutar.
- Aparece el formulario de escritorio con una lista de tareas vacia. Anada algunas tareas usando la GUI.
- Abra un navegador y navegue a
http://localhost:8080. - Ver las mismas tareas renderizadas como HTML. Anada o elimine tareas desde el navegador.
- Vuelva al escritorio — los datos se comparten en tiempo real.
Descripcion General de la Arquitectura
TForm + TBrowse"] --- C["Datos Compartidos
Arreglo aTodos"] B["TWebServer
Puerto 8080"] --- C end D["Navegador
http://localhost:8080"] -->|"HTTP GET/POST/DELETE"| B A -->|"Acceso directo al arreglo"| C style A fill:#58a6ff,stroke:#388bfd,color:#0d1117 style B fill:#3fb950,stroke:#2ea043,color:#0d1117 style C fill:#d2a8ff,stroke:#bc8cff,color:#0d1117 style D fill:#f0883e,stroke:#d18616,color:#0d1117
Para aplicaciones web mas grandes, use oServer:Static( "/assets", "./www" ) para servir archivos CSS, JavaScript e imagenes desde una carpeta local en lugar de incrustar HTML en cadenas.
Listo para anadir inteligencia a su aplicacion? Continue al tutorial de Integracion de IA para conectarse a LLMs locales y ejecutar inferencia directamente desde su aplicacion.