Vai al contenuto

📘 HIX - Riferimento completo degli helper U*

Gli helper U* sono funzioni globali accessibili da qualsiasi route, controller o file .hrb senza bisogno di passare oReq come parametro. Il dispatcher chiama HIX_SetRequest(oReq) prima di eseguire ogni action, così gli helper hanno sempre accesso alla request del thread corrente.


1. Lettura della request

Dati di input

Funzione Ritorna Descrizione
UMethod() C Metodo HTTP in maiuscolo: "GET", "POST", "PUT", "DELETE", "PATCH"
UPath() C Path senza query string: "/api/users/42"
UQuery() C Query string grezza: "page=1&limit=10"
UGet(cKey, xDef) X Parametro della query string. Senza argomenti ritorna l'hash completo
UPost(cKey, xDef) X Campo del body POST (form o JSON). Senza argomenti ritorna l'hash completo
UParam(cKey, xDef) C Variabile di route :var. Senza default restituisce errore 400 se mancante
UHeader(cKey, xDef) C Header HTTP (case-insensitive)
UCookie(cName, xDef) C Cookie della request (analizzato una volta, lazy)
UBody() C Body grezzo come stringa
UJson() H/A Body analizzato come JSON; NIL se il body non è JSON valido
UContentType() C Content-Type in minuscolo: "application/json"
UContentLength() N Lunghezza del body in byte
UFiles() A Array di hash dei file caricati (multipart). Vedi sezione upload
URequest() O Oggetto THixRequest del thread corrente (accesso low-level)
UContext() O THixContext della catena middleware corrente (accesso a oCtx:hData); NIL se non in catena MW

UGet / UPost senza argomenti: ritornano un hash con tutti i campi.

// Ottieni tutti i parametri GET in una volta
hParams := UGet()   // { "page" => "1", "limit" => "10" }

// Ottieni un campo con default
cNome := UPost( "nome", "Anonimo" )

// Variabile di route con default sicuro
nId := Val( UParam( "id", "0" ) )

// Variabile di route senza default - genera errore 400 se mancante
cSlug := UParam( "slug" )

UParam con indice numerico: quando la route usa *, il wildcard viene catturato come _1.

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

Content type e negoziazione

Funzione Ritorna Descrizione
UIsGet() L .T. se il metodo è GET
UIsPost() L .T. se il metodo è POST
UIsAjax() L .T. se X-Requested-With: XMLHttpRequest
UIsHttps() L .T. se la connessione è HTTPS
UScheme() C "http" o "https"
UIsJson() L .T. se il Content-Type è application/json
UIsForm() L .T. se il Content-Type è application/x-www-form-urlencoded
UIsMultipart() L .T. se il Content-Type è multipart/form-data
UWantsJson() L .T. se il client preferisce JSON (header Accept o AJAX)

Dati del client

Funzione Ritorna Descrizione
UIP() C IP reale del client (rispetta X-Forwarded-For se mode=proxied)
UHost() C Hostname della request (header Host)
UPort() N Porta del server

Upload multipart

aFiles := UFiles()
FOR EACH hFile IN aFiles
   // hFile["name"]  -> nome del campo
   // hFile["data"]  -> contenuto binario
   // hFile["mime"]  -> Content-Type del file
   // hFile["size"]  -> dimensione in byte
   hb_MemoWrit( "uploads/" + hFile["name"], hFile["data"] )
NEXT

2. Invio delle risposte

Risposte dirette

Funzione Descrizione
USendJson(xData [, nStatus]) JSON 200. xData può essere hash, array o stringa
USendHtml(cHtml [, nStatus]) HTML 200
USendText(cText [, nStatus]) text/plain 200
USendView(cView [, hVars]) Renderizza il template e invia HTML
USendEmpty() 204 No Content
USendError(nStatus, cDetail) Errore HTTP con messaggio di dettaglio
URedirect(cUrl [, nStatus]) Redirect (302 di default)
// Risposta JSON semplice
USendJson( { "ok" => .T. } )

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

// Redirect permanente
URedirect( "/new-url", 301 )

// Errore HTTP
USendError( 403, "Permessi insufficienti" )

Controllo fine del buffer

Quando hai bisogno di costruire la risposta passo dopo passo prima di inviarla:

Funzione Descrizione
UWrite(cText) Accumula testo nel buffer di risposta
UEcho(cText) Alias di UWrite
USetStatus(nStatus) Imposta lo status HTTP del buffer
USetMime(cMime) Imposta il MIME del buffer ("json", "html", "text" o MIME completo)
UGetMime() Ritorna il MIME attualmente configurato
USetHeader(cKey, cVal) Aggiunge un header extra alla risposta
UFlush() Invia il buffer accumulato come chunk (avvia lo streaming se è la prima volta)
// Costruisci JSON manualmente
USetStatus( 201 )
USetMime( "json" )
USetHeader( "X-Request-Id", "abc123" )
UWrite( hb_jsonEncode( { "created" => .T. } ) )
// Il dispatcher invia il buffer quando l'action termina
Funzione Descrizione
USetCookie(cName, cVal, nMaxAge) Scrive Set-Cookie nella risposta

nMaxAge: - 0 - cookie di sessione (senza Max-Age) - -1 - scade immediatamente (Max-Age=0) - > 0 - durata in secondi

I flag HttpOnly; SameSite=Lax; Path=/ vengono aggiunti automaticamente.

USetCookie( "session_id", cSid, 3600 )   // 1 ora
USetCookie( "pref", "dark", 0 )          // sessione
USetCookie( "old_cookie", "", -1 )       // scadenza

3. Streaming a chunk

Per SSE, download progressivi o risposte di lunga durata:

Funzione Descrizione
USendStreamStart(cMime, nStatus, hExtra) Avvia una risposta a chunk; header extra in hExtra
USendChunk(cData) Invia un chunk di dati
USendStreamEnd() Chiude lo stream (chunk di lunghezza zero)
// 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. Sessione

Funzione Descrizione
USession() Ritorna un oggetto proxy con metodi Get/Set/Save/Destroy
USession(cKey) Legge un valore di sessione; NIL se non esiste
USession(cKey, xDef) Legge un valore con default
// Leggi un campo
cUser := USession( "user" )

// Scrivi e salva
USession():Set( "user", "carles" )
USession():Set( "role", "admin" )
USession():Save()   // rinnova il TTL ed emette il Set-Cookie

// Distruggi la sessione
USession():Destroy()

Richiede che HIX_MwSession sia registrato come middleware sulla route.


5. JWT

Funzione Descrizione
UJwt() Ritorna l'hash completo del payload JWT; NIL se non c'è JWT
UJwt(cKey) Ritorna un claim dal payload; NIL se non esiste
UJwt(cKey, xDef) Ritorna un claim con default
UHasScope(cScope) .T. se il JWT include lo scope nel campo scope
// Leggi un claim
cSub  := UJwt( "sub" )
nExp  := UJwt( "exp", 0 )

// Controlla lo scope
IF ! UHasScope( "read:products" )
   USendError( 403, "Scope insufficiente" )
   RETURN
ENDIF

Richiede che HixMwJwt sia registrato come middleware sulla route.


6. Autenticazione e ruoli

Disponibile quando il middleware HIX_MwAuth o HIX_MwIsAuth è attivo.

Funzione Descrizione
UCurrentUser() Hash completo dell'utente autenticato; NIL se non c'è sessione
UAuthUser() Hash dell'utente della request (impostato dal middleware); NIL se non autenticato
UAuthUser(cKey) Campo dell'hash utente
UAuthUser(cKey, xDef) Campo dell'hash con default
UHasRole(cRole) .T. se l'utente ha il ruolo (accesso completo)
UHasRole(cRole, cOp) .T. se l'utente ha il ruolo con l'operazione specificata
UGetRoles() Hash dei ruoli utente: { "admin" => "", "editor" => "read;write" }
UAuthLogout() Distrugge la sessione e azzera l'utente corrente
// Controlla il ruolo
IF ! UHasRole( "admin" )
   USendError( 403, "Solo amministratori" )
   RETURN
ENDIF

// Controlla il ruolo con operazione granulare
IF ! UHasRole( "products", "delete" )
   USendError( 403, "Nessun permesso di eliminazione" )
   RETURN
ENDIF

// Leggi i dati utente
hUser := UAuthUser()
cEmail := UAuthUser( "email", "" )

// Logout
UAuthLogout()
URedirect( "/login" )

7. Validazione

Costruire un validator

Funzione Fonte dei dati
UValidate(hRules) POST (form o JSON)
UValidatePost(hRules) POST esplicito
UValidateGet(hRules) Query string
UValidateParams(hRules) Query string + variabili di route unite
UValidateJson(hRules) Body JSON esplicito
UValidateInput(hRules) Equivalente a UValidatePost (body form POST)
UValidateOrFail(hRules) POST - risponde automaticamente 422 se fallisce; ritorna NIL

Tutte accettano un secondo parametro opzionale hSanitate con regole di sanificazione.

Flusso 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   // ha già risposto 422

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

Gestione manuale degli errori

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

Regole disponibili

required            campo obbligatorio (non vuoto)
string              tipo stringa
integer             intero
numeric             numero (int o decimale)
boolean             logico
array               array
min:N               stringa: lunghezza >= N  /  numero: valore >= N
max:N               stringa: lunghezza <= N  /  numero: valore <= N
minlen:N            lunghezza stringa >= N
maxlen:N            lunghezza stringa <= N
between:N:M         numero compreso tra N e M
email               formato email
url                 inizia con http:// o https://
ip                  IPv4 valido
regex:PATTERN       espressione regolare Harbour
in:a,b,c            valore nella lista
notin:a,b           valore non nella lista
field               include il campo in DataFields() se valido

Sanificazione (applicata prima della validazione):

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

Predicati rapidi

Funzione Descrizione
UIsMail(cStr) .T. se cStr ha formato email
UIsNumeric(uValue) .T. se il valore è numerico (numero o stringa numerica)
UIsInteger(uValue) .T. se il valore è un intero
UIsUrl(cStr) .T. se inizia con http:// o https://
UIsIp(cStr) .T. se è un IPv4 valido

8. View / Template

Funzione Descrizione
USendView(cView [, hVars]) Renderizza il template e invia la risposta HTML
UView(cView [, hVars]) Renderizza il template e ritorna l'HTML come stringa

I template si trovano in www/views/ con estensione .html.

// Renderizza e invia
USendView( "users/list.html" )

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

// Ottieni solo l'HTML (per comporre partial)
cPartial := UView( "partials/header.html", { "cTitle" => "My app" } )
USendHtml( cPartial + "<main>content</main>" )

Formato del template:

@args cName, nAge

<html>
<body>
  <h1>Edit: {{ cName }}</h1>
  <p>Age: {{ hb_NToS(nAge) }}</p>
</body>
</html>

9. Helper per le view

Conversione di tipo

Funzione Descrizione
UStr(u) Converte qualsiasi tipo Harbour in stringa (C, N, L, D, A, H)
UDateToHtml(dData) Data Harbour in stringa "YYYY-MM-DD" per input HTML
ULogicToHtmlChecked(lValue) .T."checked", .F."" (per checkbox)
UHtmlEncode(cText) Effettua l'escape delle entità HTML (&, <, >, ", ') in un passaggio
UOsFileName(cFileName) Normalizza i separatori di path al separatore del sistema operativo

Select HTML

// UHashToHtmlSelect( aHash, cSelect, cKey, cValue )
// aHash: array di hash con campi key e value
// cSelect: valore attualmente selezionato
// cKey: nome del campo chiave in ogni hash (default "key")
// cValue: nome del campo valore in ogni hash (default "value")

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

Route con nome

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

10. CSRF

Protezione dei form HTML contro attacchi Cross-Site Request Forgery.

Funzione Descrizione
UCsrfToHtml([cToken]) Genera <input type="hidden" name="_csrf" value="...">
HIX_CsrfMakeToken([cData]) Genera un token CSRF firmato con la chiave csrf del HIX_Keys store
HIX_CsrfValidToken(cToken [, nLapsus]) .T. se il token è valido. nLapsus in secondi (0 = nessuna scadenza)
HIX_CsrfGenRandom([nLen]) Genera una stringa casuale di nLen byte
// Nell'action GET che serve il form
USendView( "form.html", { "cCsrf" => UCsrfToHtml() } )

// Nel template
// {{ cCsrf }}   -- emette l'<input hidden>

// Nell'action POST che processa il form
IF ! HIX_CsrfValidToken( UPost( "_csrf" ), 3600 )
   USendError( 403, "Token CSRF non valido" )
   RETURN
ENDIF

11. Resource ID

Firma un ID opaco così che non sia prevedibile nei form HTML.

Funzione Descrizione
UResourceToHtml(cId) Genera <input type="hidden" name="_resource_id" value="..."> con ID firmato
UGetResource([cToken]) Valida il token e ritorna l'ID originale; "" se non valido
// Nella view (lista record)
// {{ UResourceToHtml( hb_NToS(nId) ) }}

// Nell'action POST (delete, edit, ...)
cId := UGetResource()   // legge _resource_id dal POST automaticamente
IF Empty( cId )
   USendError( 400, "Resource ID non valido" )
   RETURN
ENDIF
nId := Val( cId )

12. Messaggi flash

Messaggi di validazione temporanei per form, memorizzati in sessione e distrutti quando letti.

Metodo Descrizione
UFlash([cFormId]) Crea un oggetto TFlash per il form specificato
oFlash:Set(cKey, xVal) Salva un valore flash
oFlash:Get(cKey [, xDef]) Legge e cancella il valore flash
oFlash:Has(cKey) .T. se il valore esiste
oFlash:Delete(cKey) Cancella un valore senza leggerlo
oFlash:Clear() Cancella tutti i valori per il form
oFlash:Save() Persiste le modifiche nella sessione
oFlash:Destroy() Distruttore: salva automaticamente all'uscita dallo scope
// Salva errore nel POST
oFlash := UFlash( "login-form" )
oFlash:Set( "error", "Credenziali errate" )
oFlash:Set( "email", UPost( "email" ) )
oFlash:Save()
URedirect( "/login" )

// Leggi nel successivo GET
oFlash := UFlash( "login-form" )
cError := oFlash:Get( "error", "" )
cEmail := oFlash:Get( "email", "" )

13. Ambiente e configurazione

Funzione Descrizione
UEnv() Ambiente corrente: "dev" o "prod"
UIsDev() .T. se UEnv() == "dev"
UIsProd() .T. se UEnv() == "prod"
UConfig(cKey [, xDef]) Valore da THixConfig per nome campo
UMwConfig(cSection, cKey [, xDef]) Valore dalla sezione setup di www/middlewares/config.json
UNow() Timestamp corrente come stringa "YYYYMMDDHHmmss"
URoot() Nome della cartella web root (default "www")
URootPath() Percorso assoluto alla web root con separatore finale
IF UIsDev()
   l( "Debug: " + hb_jsonEncode( hData ) )
ENDIF

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

14. Tabella di riferimento rapido

Lettura della 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()

Rileva tipo

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

Invio risposta

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

Controllo 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()

Sessione e autenticazione

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()

Validazione

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

View e helper

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()