00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Template

I pacchetti template di Go

Go include due pacchetti per i template: text/template per il testo generico e html/template per l’HTML con protezione automatica contro le iniezioni XSS. La sintassi e identica, ma html/template effettua l’escaping automatico dei dati inseriti nell’HTML.

Sintassi base: {{ }}

Le azioni nei template sono racchiuse tra doppie parentesi graffe. Il punto (.) rappresenta il dato corrente passato al template:

package main

import (
    "os"
    "text/template"
)

func main() {
    // Template semplice con il punto
    tmpl := template.Must(template.New("saluto").Parse(
        "Ciao, {{.}}! Benvenuto.\n",
    ))
    tmpl.Execute(os.Stdout, "Mario") // Ciao, Mario! Benvenuto.

    // Template con struct
    type Persona struct {
        Nome string
        Eta  int
    }

    tmpl2 := template.Must(template.New("persona").Parse(
        "{{.Nome}} ha {{.Eta}} anni.\n",
    ))
    tmpl2.Execute(os.Stdout, Persona{Nome: "Luigi", Eta: 25})
}

Azioni e pipeline

Le azioni sono comandi all’interno di {{ }}. Le pipeline collegano piu comandi con il simbolo |:

package main

import (
    "os"
    "text/template"
)

func main() {
    tmpl := template.Must(template.New("pipeline").Parse(`
Messaggio: {{.Testo}}
Maiuscolo: {{.Testo | printf "%q"}}
Lunghezza: {{.Testo | len}}
`))

    dati := struct {
        Testo string
    }{
        Testo: "Ciao dal template",
    }

    tmpl.Execute(os.Stdout, dati)
}

Le funzioni built-in includono print, printf, println, len, index, html, js, urlquery e molte altre.

Variabili nei template

Possiamo dichiarare e usare variabili nei template con la sintassi $nome:

package main

import (
    "os"
    "text/template"
)

func main() {
    tmpl := template.Must(template.New("variabili").Parse(`
{{$nome := .Nome}}
{{$citta := .Citta}}
L'utente {{$nome}} vive a {{$citta}}.
Il suo indirizzo completo e: {{$nome}}, {{$citta}}, {{.Paese}}.
`))

    dati := map[string]string{
        "Nome":  "Anna",
        "Citta": "Roma",
        "Paese": "Italia",
    }

    tmpl.Execute(os.Stdout, dati)
}

Range: iterare su collezioni

L’azione range permette di iterare su slice, array e map:

package main

import (
    "os"
    "text/template"
)

type Prodotto struct {
    Nome   string
    Prezzo float64
}

func main() {
    tmpl := template.Must(template.New("lista").Parse(`
Lista della spesa:
{{range .Prodotti}}- {{.Nome}}: {{printf "%.2f" .Prezzo}} EUR
{{end}}
Totale prodotti: {{len .Prodotti}}

Con indice:
{{range $i, $p := .Prodotti}}  {{$i}}. {{$p.Nome}} ({{printf "%.2f" $p.Prezzo}} EUR)
{{end}}
Messaggio se vuoto:
{{range .Vuoto}}Ha elementi{{else}}La lista e vuota{{end}}
`))

    dati := struct {
        Prodotti []Prodotto
        Vuoto    []string
    }{
        Prodotti: []Prodotto{
            {"Pane", 1.50},
            {"Latte", 1.80},
            {"Pasta", 0.90},
        },
        Vuoto: nil,
    }

    tmpl.Execute(os.Stdout, dati)
}

If: condizioni nei template

L’azione if permette di controllare il flusso in base a condizioni:

package main

import (
    "os"
    "text/template"
)

type Utente struct {
    Nome    string
    Premium bool
    Eta     int
    Bio     string
}

func main() {
    tmpl := template.Must(template.New("condizioni").Parse(`
Utente: {{.Nome}}
{{if .Premium}}Account Premium attivo!{{else}}Account Standard{{end}}

{{if .Bio}}Bio: {{.Bio}}
{{else}}Nessuna bio disponibile
{{end}}

{{if gt .Eta 18}}Utente maggiorenne{{else}}Utente minorenne{{end}}
`))

    utente := Utente{
        Nome:    "Giulia",
        Premium: true,
        Eta:     22,
        Bio:     "",
    }

    tmpl.Execute(os.Stdout, utente)
}

Le funzioni di confronto disponibili sono: eq (==), ne (!=), lt (<), le (<=), gt (>), ge (>=).

Funzioni personalizzate

Possiamo registrare funzioni personalizzate da usare nei template:

package main

import (
    "os"
    "strings"
    "text/template"
    "time"
)

func main() {
    funzioni := template.FuncMap{
        "maiuscolo": strings.ToUpper,
        "ripeti":    strings.Repeat,
        "ora":       func() string { return time.Now().Format("15:04:05") },
        "somma":     func(a, b int) int { return a + b },
    }

    tmpl := template.Must(
        template.New("funzioni").Funcs(funzioni).Parse(`
Nome: {{.Nome | maiuscolo}}
Decorazione: {{ripeti "=" 30}}
Ora corrente: {{ora}}
Somma: {{somma 10 20}}
`))

    dati := struct {
        Nome string
    }{Nome: "Mario"}

    tmpl.Execute(os.Stdout, dati)
}

Importante: le funzioni devono essere registrate con Funcs() prima di chiamare Parse().

Template annidati

I template possono includere altri template con define e template:

package main

import (
    "os"
    "text/template"
)

func main() {
    tmpl := template.Must(template.New("pagina").Parse(`
{{define "header"}}
=============================
  {{.Titolo}}
=============================
{{end}}

{{define "footer"}}
-----------------------------
  (c) {{.Anno}} - {{.Autore}}
-----------------------------
{{end}}

{{template "header" .}}

Contenuto della pagina:
{{range .Paragrafi}}
  * {{.}}
{{end}}

{{template "footer" .}}
`))

    dati := struct {
        Titolo    string
        Anno      int
        Autore    string
        Paragrafi []string
    }{
        Titolo:    "Guida Go",
        Anno:      2026,
        Autore:    "Team Go",
        Paragrafi: []string{"Introduzione", "Template", "Conclusione"},
    }

    tmpl.Execute(os.Stdout, dati)
}

Parsing di file template

Per caricare template da file esterni:

package main

import (
    "html/template"
    "net/http"
)

func main() {
    // Carica un singolo file
    tmpl := template.Must(template.ParseFiles("templates/pagina.html"))

    // Carica piu file
    tmpl = template.Must(template.ParseFiles(
        "templates/layout.html",
        "templates/header.html",
        "templates/footer.html",
    ))

    // Carica con pattern glob
    tmpl = template.Must(template.ParseGlob("templates/*.html"))

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        dati := map[string]string{
            "Titolo":    "Home Page",
            "Contenuto": "Benvenuto nel sito!",
        }
        tmpl.ExecuteTemplate(w, "layout.html", dati)
    })

    http.ListenAndServe(":8080", nil)
}

html/template per la sicurezza

Il pacchetto html/template protegge automaticamente contro le iniezioni XSS:

package main

import (
    "html/template"
    "os"
)

func main() {
    tmpl := template.Must(template.New("sicuro").Parse(`
<h1>{{.Titolo}}</h1>
<p>{{.Contenuto}}</p>
`))

    dati := struct {
        Titolo    string
        Contenuto string
    }{
        Titolo:    "<script>alert('XSS')</script>",
        Contenuto: "Testo con <b>HTML</b> & caratteri speciali",
    }

    tmpl.Execute(os.Stdout, dati)
    // L'output avra i caratteri HTML escapati automaticamente
}

Se dovete inserire HTML fidato, usate il tipo template.HTML:

dati := struct {
    Sicuro   template.HTML
    Insicuro string
}{
    Sicuro:   template.HTML("<strong>HTML fidato</strong>"),
    Insicuro: "<script>alert('XSS')</script>",
}

Conclusione

Il sistema di template di Go e potente e sicuro. text/template e adatto per generare testo generico, email e file di configurazione, mentre html/template e essenziale per generare HTML sicuro. Le funzioni personalizzate, i template annidati e il parsing da file rendono il sistema flessibile e adatto a qualsiasi esigenza. Ricordate di usare sempre html/template quando generate HTML per proteggervi dalle iniezioni XSS.