Saltar a contenido

📘 HIX - Referencia completa de helpers U*

Los helpers U* son funciones globales accesibles desde cualquier ruta, controlador o archivo .hrb sin necesidad de pasar oReq como parámetro. El dispatcher llama HIX_SetRequest(oReq) antes de ejecutar cada acción, de modo que los helpers siempre tienen acceso al request del hilo actual.


1. Lectura del request

Datos de entrada

Funcion Retorna Descripcion
UMethod() C Metodo HTTP en mayusculas: "GET", "POST", "PUT", "DELETE", "PATCH"
UPath() C Path sin query string: "/api/users/42"
UQuery() C Query string raw: "page=1&limit=10"
UGet(cKey, xDef) X Parametro de query string. Sin argumentos devuelve hash completo
UPost(cKey, xDef) X Campo de body POST (form o JSON). Sin argumentos devuelve hash completo
UParam(cKey, xDef) C Variable de ruta :var. Sin default lanza error 400 si no existe
UHeader(cKey, xDef) C Cabecera HTTP (case-insensitive)
UCookie(cName, xDef) C Cookie del request (parseada una sola vez, lazy)
UBody() C Body raw como string
UJson() H/A Body parseado como JSON; NIL si el body no es JSON valido
UContentType() C Content-Type en minusculas: "application/json"
UContentLength() N Longitud en bytes del body
UFiles() A Array de hashes de archivos subidos (multipart). Ver seccion uploads
URequest() O Objeto THixRequest del hilo actual (acceso de bajo nivel)
UContext() O THixContext del middleware actual (acceso a oCtx:hData); NIL si no esta en cadena MW

UGet / UPost sin argumentos: devuelven un hash con todos los campos.

// Obtener todos los parametros GET de una vez
hParams := UGet()   // { "page" => "1", "limit" => "10" }

// Obtener un campo con default
cNombre := UPost( "nombre", "Anonimo" )

// Variable de ruta con default seguro
nId := Val( UParam( "id", "0" ) )

// Variable de ruta sin default — lanza 400 si falta
cSlug := UParam( "slug" )

UParam con indice numerico: cuando la ruta usa * el comodin se captura como _1.

oSrv:AddRouteGet( "static", "/static/*", {||
   cFile := UParam( 1 )   // equivale a UParam("_1")
} )

Tipo y negociacion de contenido

Funcion Retorna Descripcion
UIsGet() L .T. si el metodo es GET
UIsPost() L .T. si el metodo es POST
UIsAjax() L .T. si X-Requested-With: XMLHttpRequest
UIsHttps() L .T. si la conexion es HTTPS
UScheme() C "http" o "https"
UIsJson() L .T. si el Content-Type es application/json
UIsForm() L .T. si el Content-Type es application/x-www-form-urlencoded
UIsMultipart() L .T. si el Content-Type es multipart/form-data
UWantsJson() L .T. si el cliente prefiere JSON (cabecera Accept o AJAX)

Datos del cliente

Funcion Retorna Descripcion
UIP() C IP real del cliente (respeta X-Forwarded-For si mode=proxied)
UHost() C Hostname del request (cabecera Host)
UPort() N Puerto del servidor

Uploads multipart

aFiles := UFiles()
FOR EACH hFile IN aFiles
   // hFile["name"]  -> nombre del campo
   // hFile["data"]  -> contenido binario
   // hFile["mime"]  -> Content-Type del archivo
   // hFile["size"]  -> tamano en bytes
   hb_MemoWrit( "uploads/" + hFile["name"], hFile["data"] )
NEXT

2. Enviar respuestas

Respuestas directas

Funcion Descripcion
USendJson(xData [, nStatus]) JSON 200. xData puede ser hash, array o string
USendHtml(cHtml [, nStatus]) HTML 200
USendText(cText [, nStatus]) text/plain 200
USendView(cView [, hVars]) Renderiza template y envia HTML
USendEmpty() 204 No Content
USendError(nStatus, cDetail) Error HTTP con mensaje de detalle
URedirect(cUrl [, nStatus]) Redireccion (302 por defecto)
// Respuesta JSON simple
USendJson( { "ok" => .T. } )

// Con status custom
USendJson( { "id" => 42, "name" => "Test" }, 201 )

// Redireccion permanente
URedirect( "/nueva-url", 301 )

// Error HTTP
USendError( 403, "Sin permisos" )

Control fino del buffer

Cuando necesitas construir la respuesta paso a paso antes de enviarla:

Funcion Descripcion
UWrite(cText) Acumula texto en el buffer de respuesta
UEcho(cText) Alias de UWrite
USetStatus(nStatus) Fija el status HTTP del buffer
USetMime(cMime) Fija el MIME del buffer ("json", "html", "text" o MIME completo)
UGetMime() Retorna el MIME configurado actualmente
USetHeader(cKey, cVal) Agrega cabecera extra a la respuesta
UFlush() Envia el buffer acumulado como chunk (inicia streaming si es la primera vez)
// Construir JSON manualmente
USetStatus( 201 )
USetMime( "json" )
USetHeader( "X-Request-Id", "abc123" )
UWrite( hb_jsonEncode( { "created" => .T. } ) )
// El dispatcher envia el buffer al terminar la accion

Cookies en respuesta

Funcion Descripcion
USetCookie(cName, cVal, nMaxAge) Escribe Set-Cookie en la respuesta

nMaxAge: - 0 — cookie de sesion (sin Max-Age) - -1 — expirar inmediatamente (Max-Age=0) - > 0 — duracion en segundos

Los flags HttpOnly; SameSite=Lax; Path=/ se agregan automaticamente.

USetCookie( "session_id", cSid, 3600 )   // 1 hora
USetCookie( "pref", "dark", 0 )          // sesion
USetCookie( "old_cookie", "", -1 )       // expirar

3. Streaming chunked

Para SSE, descargas progresivas o respuestas de larga duracion:

Funcion Descripcion
USendStreamStart(cMime, nStatus, hExtra) Inicia respuesta chunked; cabeceras extras en hExtra
USendChunk(cData) Envia un fragmento de datos
USendStreamEnd() Cierra el stream (chunk de longitud cero)
// SSE — Server-Sent Events
oSrv:AddRouteGet( "events", "/events", {||
   LOCAL i := 0
   USendStreamStart( "text/event-stream", 200, ;
      { "Cache-Control" => "no-cache", "X-Accel-Buffering" => "no" } )
   DO WHILE i < 10
      i++
      USendChunk( "data: " + hb_jsonEncode( { "n" => i } ) + hb_eol() + hb_eol() )
      hb_idleSleep( 1 )
   ENDDO
   USendStreamEnd()
} )

4. Sesion

Funcion Descripcion
USession() Devuelve un objeto proxy con metodos Get/Set/Save/Destroy
USession(cKey) Lee un valor de la sesion; NIL si no existe
USession(cKey, xDef) Lee un valor con default
// Leer un campo
cUser := USession( "user" )

// Escribir y guardar
USession():Set( "user", "carles" )
USession():Set( "role", "admin" )
USession():Save()   // renueva TTL y emite Set-Cookie

// Destruir sesion
USession():Destroy()

Requiere que HIX_MwSession este registrado como middleware en la ruta.


5. JWT

Funcion Descripcion
UJwt() Devuelve el hash completo del payload JWT; NIL si no hay JWT
UJwt(cKey) Devuelve un claim del payload; NIL si no existe
UJwt(cKey, xDef) Devuelve un claim con default
UHasScope(cScope) .T. si el JWT incluye el scope indicado en el campo scope
// Leer claim
cSub  := UJwt( "sub" )
nExp  := UJwt( "exp", 0 )

// Verificar scope
IF ! UHasScope( "read:products" )
   USendError( 403, "Scope insuficiente" )
   RETURN
ENDIF

Requiere que HixMwJwt este registrado como middleware en la ruta.


6. Autenticacion y roles

Disponibles cuando el middleware HIX_MwAuth o HIX_MwIsAuth esta activo.

Funcion Descripcion
UCurrentUser() Hash completo del usuario autenticado; NIL si no hay sesion
UAuthUser() Hash del usuario del request (set por middleware); NIL si no autenticado
UAuthUser(cKey) Campo del hash del usuario
UAuthUser(cKey, xDef) Campo del hash con default
UHasRole(cRole) .T. si el usuario tiene el rol (acceso completo)
UHasRole(cRole, cOp) .T. si el usuario tiene el rol con la operacion indicada
UGetRoles() Hash de roles del usuario: { "admin" => "", "editor" => "read;write" }
UAuthLogout() Destruye la sesion y limpia el usuario actual
// Verificar rol
IF ! UHasRole( "admin" )
   USendError( 403, "Solo administradores" )
   RETURN
ENDIF

// Verificar rol con operacion granular
IF ! UHasRole( "products", "delete" )
   USendError( 403, "Sin permiso de borrado" )
   RETURN
ENDIF

// Leer datos del usuario
hUser := UAuthUser()
cEmail := UAuthUser( "email", "" )

// Cerrar sesion
UAuthLogout()
URedirect( "/login" )

7. Validacion

Construir un validador

Funcion Fuente de datos
UValidate(hRules) POST (form o JSON)
UValidatePost(hRules) POST explicito
UValidateGet(hRules) Query string
UValidateParams(hRules) Query string + variables de ruta fusionadas
UValidateJson(hRules) Body JSON explicito
UValidateInput(hRules) Equivalente a UValidatePost (form body POST)
UValidateOrFail(hRules) POST — responde 422 automaticamente si falla; devuelve NIL

Todas aceptan un segundo parametro opcional hSanitate con reglas de sanitizacion.

Flujo tipico

FUNCTION _CreateUser()
   LOCAL oVal := UValidateOrFail( { ;
      "name"  => "required|string|max:100", ;
      "email" => "required|string|email",   ;
      "age"   => "required|integer|min:18"  ;
   } )
   IF oVal == NIL ; RETURN NIL ; ENDIF   // ya respondio 422

   cName  := oVal:Get( "name" )
   cEmail := oVal:Get( "email" )
   nAge   := oVal:Get( "age" )
   // ...
   USendJson( { "ok" => .T. }, 201 )
RETURN NIL

Manejo manual de errores

LOCAL oVal := UValidatePost( hRules )
IF ! oVal:Make()
   USendJson( { "errors" => oVal:GetErrorsJson() }, 422 )
   RETURN
ENDIF

Reglas disponibles

required            campo obligatorio (no vacio)
string              tipo string
integer             entero
numeric             numero (int o decimal)
boolean             logico
array               array
min:N               string: longitud >= N  /  numero: valor >= N
max:N               string: longitud <= N  /  numero: valor <= N
minlen:N            longitud string >= N
maxlen:N            longitud string <= N
between:N:M         numero entre N y M
email               formato email
url                 empieza por http:// o https://
ip                  IPv4 valida
regex:PATRON        expresion regular Harbour
in:a,b,c            valor en lista
notin:a,b           valor no en lista
field               incluye el campo en DataFields() si valido

Sanitizacion (se aplican antes de validar):

trim                AllTrim()
lower               Lower()
upper               Upper()

Predicados rapidos

Funcion Descripcion
UIsMail(cStr) .T. si cStr tiene formato de email
UIsNumeric(uValue) .T. si el valor es numerico (numero o string numerico)
UIsInteger(uValue) .T. si el valor es un entero
UIsUrl(cStr) .T. si empieza por http:// o https://
UIsIp(cStr) .T. si es una IPv4 valida

8. Vistas / Templates

Funcion Descripcion
USendView(cView [, hVars]) Renderiza el template y envia la respuesta HTML
UView(cView [, hVars]) Renderiza el template y devuelve el HTML como string

Los templates se ubican en www/views/ con extension .html.

// Renderizar y enviar
USendView( "users/list.html" )

// Con variables
USendView( "users/edit.html", { ;
   "cName" => "Carles", ;
   "nAge"  => 42        ;
} )

// Solo obtener el HTML (para componer partials)
cPartial := UView( "partials/header.html", { "cTitle" => "Mi app" } )
USendHtml( cPartial + "<main>contenido</main>" )

Formato del template:

@args cName, nAge

<html>
<body>
  <h1>Editar: {{ cName }}</h1>
  <p>Edad: {{ hb_NToS(nAge) }}</p>
</body>
</html>

9. Herramientas de vista

Conversion de tipos

Funcion Descripcion
UStr(u) Convierte cualquier tipo Harbour a string (C, N, L, D, A, H)
UDateToHtml(dFecha) Fecha Harbour a string "YYYY-MM-DD" para inputs HTML
ULogicToHtmlChecked(lValue) .T."checked", .F."" (para checkboxes)
UHtmlEncode(cText) Escapa entidades HTML (&, <, >, ", ') en una pasada
UOsFileName(cFileName) Normaliza separadores de path al separador del sistema operativo

Select HTML

// UHashToHtmlSelect( aHash, cSelect, cKey, cValue )
// aHash: array de hashes con campos clave y valor
// cSelect: valor actualmente seleccionado
// cKey: nombre del campo clave en cada hash (default "key")
// cValue: nombre del campo valor en cada hash (default "value")

aItems := { { "key" => "es", "value" => "Espanol" }, ;
            { "key" => "en", "value" => "English" } }
cHtml := UHashToHtmlSelect( aItems, "es", "key", "value" )
// <option value="" ></option>
// <option value="es" selected>Espanol</option>
// <option value="en">English</option>

Rutas nombradas

// URoute( cName, param1, param2, ... )
cUrl := URoute( "user", 42 )       // -> "/users/42"
cUrl := URoute( "post", "mi-slug" ) // -> "/posts/mi-slug"

10. CSRF

Para proteger formularios HTML contra ataques Cross-Site Request Forgery.

Funcion Descripcion
UCsrfToHtml([cToken]) Genera <input type="hidden" name="_csrf" value="...">
HIX_CsrfMakeToken([cData]) Genera un token CSRF firmado con la clave csrf del store HIX_Keys
HIX_CsrfValidToken(cToken [, nLapsus]) .T. si el token es valido. nLapsus en segundos (0 = sin expiracion)
HIX_CsrfGenRandom([nLen]) Genera un string aleatorio de nLen bytes
// En la accion GET que sirve el formulario
USendView( "form.html", { "cCsrf" => UCsrfToHtml() } )

// En el template
// {{ cCsrf }}   -- emite el <input hidden>

// En la accion POST que procesa el formulario
IF ! HIX_CsrfValidToken( UPost( "_csrf" ), 3600 )
   USendError( 403, "Token CSRF invalido" )
   RETURN
ENDIF

11. Resource ID

Firma un ID opaco para que no sea predecible en formularios HTML.

Funcion Descripcion
UResourceToHtml(cId) Genera <input type="hidden" name="_resource_id" value="..."> con el ID firmado
UGetResource([cToken]) Valida el token y devuelve el ID original; "" si invalido
// En la vista (lista de registros)
// {{ UResourceToHtml( hb_NToS(nId) ) }}

// En la accion POST (eliminar, editar, ...)
cId := UGetResource()   // lee _resource_id del POST automaticamente
IF Empty( cId )
   USendError( 400, "ID de recurso invalido" )
   RETURN
ENDIF
nId := Val( cId )

12. Flash messages

Mensajes temporales de validacion por formulario, almacenados en sesion y destruidos al leerlos.

Metodo Descripcion
UFlash([cFormId]) Crea un objeto TFlash para el formulario indicado
oFlash:Set(cKey, xVal) Guarda un valor flash
oFlash:Get(cKey [, xDef]) Lee y elimina el valor flash
oFlash:Has(cKey) .T. si existe el valor
oFlash:Delete(cKey) Elimina un valor sin leerlo
oFlash:Clear() Limpia todos los valores del formulario
oFlash:Save() Persiste los cambios en sesion
oFlash:Destroy() Destructor: guarda automaticamente al salir de scope
// Guardar error en el POST
oFlash := UFlash( "login-form" )
oFlash:Set( "error", "Credenciales incorrectas" )
oFlash:Set( "email", UPost( "email" ) )
oFlash:Save()
URedirect( "/login" )

// Leer en el GET siguiente
oFlash := UFlash( "login-form" )
cError := oFlash:Get( "error", "" )
cEmail := oFlash:Get( "email", "" )

13. Entorno y configuracion

Funcion Descripcion
UEnv() Entorno actual: "dev" o "prod"
UIsDev() .T. si UEnv() == "dev"
UIsProd() .T. si UEnv() == "prod"
UConfig(cKey [, xDef]) Valor de THixConfig por nombre de campo
UMwConfig(cSection, cKey [, xDef]) Valor de www/middlewares/config.json seccion setup
UNow() Timestamp actual como string "YYYYMMDDHHmmss"
URoot() Nombre de la carpeta web root (por defecto "www")
URootPath() Path absoluto al web root con separador final
IF UIsDev()
   l( "Debug: " + hb_jsonEncode( hData ) )
ENDIF

cPort := UConfig( "nPort", "8080" )
cKey  := UMwConfig( "auth", "session_user_key", "_auth_user" )

14. Tabla rapida de referencia

Leer request

UMethod()           UPath()             UQuery()
UGet(k,d)           UPost(k,d)          UParam(k,d)
UHeader(k,d)        UCookie(k,d)        UBody()
UJson()             UContentType()      UContentLength()
UFiles()            URequest()          UContext()

Detectar tipo

UIsGet()    UIsPost()   UIsAjax()   UIsHttps()
UIsJson()   UIsForm()   UIsMultipart()  UWantsJson()
UScheme()   UIP()       UHost()         UPort()

Enviar respuesta

USendJson(x,n)      USendHtml(c,n)      USendText(c,n)
USendView(v,h)      USendEmpty()        USendError(n,c)
URedirect(u,n)      USend(x,n,m,h)

Controlar buffer

UWrite(c)   UEcho(c)    USetStatus(n)   USetMime(c)
UGetMime()  USetHeader(k,v)  USetCookie(k,v,n)  UFlush()

Streaming

USendStreamStart(m,n,h)   USendChunk(c)   USendStreamEnd()

Sesion y auth

USession()  USession(k)  USession(k,d)
UJwt()      UJwt(k)      UJwt(k,d)     UHasScope(s)
UCurrentUser()  UAuthUser(k,d)
UHasRole(r)     UHasRole(r,op)  UGetRoles()  UAuthLogout()

Validacion

UValidate(h)    UValidatePost(h)  UValidateGet(h)
UValidateParams(h)  UValidateJson(h)  UValidateInput(h)  UValidateOrFail(h)
UIsMail(s)  UIsNumeric(v)  UIsInteger(v)  UIsUrl(s)  UIsIp(s)

Vistas y herramientas

USendView(v,h)  UView(v,h)
UStr(u)  UDateToHtml(d)  ULogicToHtmlChecked(l)  UHtmlEncode(c)
UHashToHtmlSelect(a,s,k,v)  URoute(name, ...)  UOsFileName(f)

CSRF, Resource, Flash, Config

UCsrfToHtml()       HIX_CsrfValidToken(t,n)
UResourceToHtml(id) UGetResource()
UFlash(id)
UEnv()  UIsDev()  UIsProd()  UConfig(k,d)  UMwConfig(s,k,d)
UNow()  URoot()   URootPath()