Il testo è tutto intorno a noi come sviluppatori di software. Il codice è testo, HTML è testo, XNL / JSON / YAML / TOML è testo, Markdown è testo, CSV è testo. Tutti questi formati di testo sono progettati per soddisfare sia gli uomini che le macchine. Gli esseri umani dovrebbero essere in grado di leggere e modificare i formati testuali con editor di testo semplice.
Ma ci sono molti casi in cui è necessario generare testo in un determinato formato. È possibile convertire da un formato all'altro, creare la propria DSL, generare automaticamente un codice di supporto o semplicemente personalizzare un'e-mail con informazioni specifiche dell'utente. Qualunque sia la necessità, Go è più che in grado di assisterti lungo il percorso con i suoi potenti modelli.
In questo tutorial, imparerai a conoscere i dettagli di modelli Go e come utilizzarli per generare un testo potente.
I modelli Go sono oggetti che gestiscono del testo con segnaposti speciali chiamati azioni, racchiusi tra doppie parentesi graffe: qualche azione
. Quando si esegue il modello, viene fornito con una struttura Go che contiene i dati necessari ai segnaposto.
Ecco un rapido esempio che genera battute a colpi di martello. Una battuta di knock knock ha un formato molto severo. Le uniche cose che cambiano sono l'identità del knocker e la battuta finale.
pacchetto main import ("text / template" "os") tipo Joke struct Who stringa Punchline string func main () t: = template.New ("Knock Knock Joke") text: = 'Knock Knock \ nChi è lì? .Chi chi chi? .Punchline 't.Parse (testo) barzellette: = [] Joke "Etch", "Bless you!", "Cow goes", "No, cow goes moo!", Per _, joke: = range jokes t.Execute (os.Stdout, joke) Output: Knock Knock Chi c'è? Etch Etch chi? Salute! Knock Knock Chi c'è? Cow va Cow va chi? No, la mucca va moo!
La sintassi del template è molto potente e supporta azioni come accessor di dati, funzioni, pipeline, variabili, condizionali e loop.
Gli accessor di dati sono molto semplici. Tirano fuori i dati dalla struttura che inizia. Possono scavare anche nelle strutture nidificate:
func main () family: = Famiglia Padre: Person "Tarzan", Madre: Person "Jane", ChildrenCount: 2, t: = template.New ("Padre") text: = "Il padre nome è .Father.Name "t.Parse (testo) t.Execute (os.Stdout, famiglia)
Se i dati non sono una struttura, puoi usare solo .
per accedere direttamente al valore:
func main () t: = template.New ("") t.Parse ("Anything goes: . \ n") t.Execute (os.Stdout, 1) t.Execute (os.Stdout, "two") t.Execute (os.Stdout, 3.0) t.Execute (os.Stdout, map [string] int "four": 4) Output: Qualsiasi cosa va: 1 Qualche cosa va: due Qualche cosa va: 3 Qualche cosa va: mappa [quattro: 4]
Vedremo in seguito come gestire array, slice e mappe.
Le funzioni elevano davvero ciò che puoi fare con i modelli. Esistono molte funzioni globali e puoi persino aggiungere funzioni specifiche del modello. L'elenco completo delle funzioni globali è disponibile sul sito web Go.
Ecco un esempio di come usare il printf
funzione in un modello:
func main () t: = template.New ("") t.Parse ('Mantenendo solo 2 decimali di π: printf "% .2f".') t.Execute (os.Stdout, math. Pi) Output: mantenendo solo 2 decimali di π: 3.14
Le pipeline consentono di applicare più funzioni al valore corrente. La combinazione di diverse funzioni espande in modo significativo i modi in cui è possibile suddividere e azzerare i valori.
Nel seguente codice, concateno tre funzioni. Innanzitutto, la funzione di chiamata esegue la funzione passa a Eseguire()
. Poi il len
la funzione restituisce la lunghezza del risultato della funzione di input, che in questo caso è 3. Finalmente, il printf
funzione stampa il numero di elementi.
func main () t: = template.New ("") t.Parse ('call. | len | printf "% d items"') t.Execute (os.Stdout, func () stringa return "abc") Output: 3 elementi
A volte si desidera riutilizzare il risultato di una pipeline complessa più volte. Con i modelli Go, puoi definire una variabile e riutilizzarla tutte le volte che vuoi. L'esempio seguente estrae il nome e il cognome dalla struttura di input, li cita e li memorizza nelle variabili $ F
e $ L
. Quindi li rende in ordine normale e inverso.
Un altro trucco chiaro qui è che passo una struttura anonima al modello per rendere il codice più conciso ed evitare di ingombrarlo con tipi che sono usati solo in un posto.
func main () t: = template.New ("") t.Parse ('$ F: = .FirstName | printf "% q" $ L: = .LastName | printf "% q" Normale: $ F $ L Reverse: $ L $ F ') t.Execute (os.Stdout, struct FirstName stringa LastName stringa "Gigi "," Sayfan ",) Uscita: Normale:" Gigi "" Sayfan "Al contrario:" Sayfan "" Gigi "
Ma non fermiamoci qui. Puoi persino avere condizioni nei tuoi modelli. C'è un se-end
azione e if-else-end
azione. La clausola if viene visualizzata se l'output della pipeline condizionale non è vuoto:
func main () t: = template.New ("") t.Parse ('if. - . else Nessun dato disponibile end') t. Execute (os.Stdout, "42") t.Execute (os.Stdout, "") Output: 42 Nessun dato disponibile
Si noti che la clausola else causa una nuova riga e il testo "Nessun dato disponibile" è notevolmente rientrato.
Anche i modelli hanno loop. Questo è super utile quando i tuoi dati contengono sezioni, mappe o altri iterabili. L'oggetto dati per un loop può essere qualsiasi oggetto Go iterable come array, slice, map o channel. La funzione intervallo consente di eseguire iterazioni sull'oggetto dati e creare un output per ciascun elemento. Vediamo come iterare su una mappa:
func main () t: = template.New ("") e: = 'Nome, Punteggi range $ k, $ v: =. $ k range $ s: = $ v , $ s end end 't.Parse (e) t.Execute (os.Stdout, map [string] [] int "Mike": 88, 77 , 99, "Betty": 54, 96, 78, "Jake": 89, 67, 93,) Output: Nome, punteggi Betty, 54,96,78 Jake, 89,67,93 Mike, 88,77,99
Come puoi vedere, lo spazio bianco iniziale è ancora un problema. Non ero in grado di trovare un modo decente per affrontarlo all'interno della sintassi del template. Richiederà la post-elaborazione. In teoria, puoi posizionare un trattino per tagliare lo spazio bianco precedente o successivo alle azioni, ma non funziona in presenza di gamma
.
I modelli di testo sono implementati nel pacchetto testo / modello. Oltre a tutto ciò che abbiamo visto finora, questo pacchetto può anche caricare modelli da file e comporre più modelli utilizzando l'azione del modello. L'oggetto Template stesso ha molti metodi per supportare casi d'uso avanzati:
A causa delle limitazioni di spazio, non andrò più in dettaglio (forse in un altro tutorial).
I modelli HTML sono definiti nel pacchetto html / template. Ha esattamente la stessa interfaccia del pacchetto di modelli di testo, ma è progettato per generare codice HTML sicuro dall'iniezione di codice. Ciò avviene disinfettando accuratamente i dati prima di incorporarli nel modello. L'ipotesi di lavoro è che gli autori dei modelli siano attendibili, ma i dati forniti al modello non possono essere considerati attendibili.
Questo è importante. Se applichi automaticamente i modelli che ricevi da fonti non attendibili, il pacchetto html / template non ti proteggerà. È tua responsabilità controllare i modelli.
Vediamo la differenza tra l'output di text / template
e html / template
. Quando si utilizza il testo / modello, è facile inserire il codice JavaScript nell'output generato.
pacchetto main import ("text / template" "os") func main () t, _: = template.New (""). Parse ("Hello, .!") d: = ""t.Execute (os.Stdout, d) Output: Ciao, !
Ma importando il html / template
invece di text / template
impedisce questo attacco, evitando i tag dello script e le parentesi:
Ciao, !
Esistono due tipi di errori: errori di analisi e errori di esecuzione. Il Parse ()
funzione analizza il testo del modello e restituisce un errore, che ho ignorato negli esempi di codice, ma nel codice di produzione si desidera rilevare questi errori in anticipo e affrontarli.
Se vuoi un'uscita rapida e sporca, allora il Dovere()
metodo prende l'output di un metodo che restituisce (* Modello, errore)
-piace Clone()
, Parse ()
, o ParseFiles ()
-e panico se l'errore non è nullo. Ecco come viene verificato un errore di analisi esplicito:
func main () e: = "I'm a bad template, " _, err: = template.New (""). Parse (e) se err! = nil msg: = "Non riuscito analisi: '% s'. \ nErrore:% v \ n "fmt.Printf (msg, e, err) Output: modello di analisi non riuscito: 'I'm a bad template, '. Errore: modello:: 1: azione inaspettata non chiusa nel comando
utilizzando Dovere()
semplicemente panico se qualcosa non va con il modello:
func main () e: = "I'm a bad template, " template.Must (template.New (""). Parse (e)) Output: panic: template:: 1: inaspettato non chiuso azione al comando
L'altro tipo di errore è un errore di esecuzione se i dati forniti non corrispondono al modello. Di nuovo, puoi controllare esplicitamente o usare Dovere()
farsi prendere dal panico. In questo caso, ti consiglio di verificare e di avere un meccanismo di recupero in atto.
Di solito, non è necessario portare giù l'intero sistema solo perché un input non soddisfa i requisiti. Nell'esempio seguente, il modello si aspetta un campo chiamato Nome
sulla struttura dei dati, ma fornisco una struttura con un campo chiamato Nome e cognome
.
func main () e: = "Deve esserci un nome: .Name" t, _: = template.New (""). Parse (e) err: = t.Execute (os.Stdout, struct FullName string "Gigi Sayfan",) se err! = nil fmt.Println ("Impossibile eseguire.", err) Output: deve esserci un nome: impossibile eseguire. template:: 1: 24: eseguendo "" a <.Name>: impossibile valutare il campo Name in type struct FullName string
Go ha un sistema di templating potente e sofisticato. È usato con grande efficacia in molti grandi progetti come Kubernetes e Hugo. Il pacchetto html / template offre una struttura sicura e industriale per sanificare l'output dei sistemi basati sul web. In questo tutorial, abbiamo trattato tutte le basi e alcuni casi d'uso intermedi.
Ci sono ancora funzioni più avanzate nei pacchetti di modelli che attendono di essere sbloccati. Gioca con i modelli e inseriscili nei tuoi programmi. Sarai piacevolmente sorpreso di quanto sia sintetico e leggibile il tuo codice di generazione del testo.