Tekst genereren met go-sjablonen

Overzicht

Tekst is overal om ons heen als softwareontwikkelaars. Code is tekst, HTML is tekst, XNL / JSON / YAML / TOML is tekst, Markdown is tekst, CSV is tekst. Al deze tekstformaten zijn ontworpen om tegemoet te komen aan zowel mensen als machines. Mensen moeten in staat zijn tekstuele formaten te lezen en te bewerken met teksteditors. 

Maar er zijn veel gevallen waarin u tekst in een bepaald formaat moet genereren. U kunt van het ene formaat naar het andere converteren, uw eigen DSL maken, automatisch een aantal helpercodes genereren of een e-mail aanpassen met gebruikerspecifieke informatie. Wat de behoefte ook is, Go is meer dan in staat om u onderweg te helpen met zijn krachtige sjablonen. 

In deze zelfstudie leert u meer over het reilen en zeilen van Go-sjablonen en hoe u ze kunt gebruiken voor het genereren van krachtige tekst.

Wat zijn Go-sjablonen?

Go-sjablonen zijn objecten die tekst beheren met speciale tijdelijke aanduidingen, acties genaamd, die worden omsloten door dubbele accolades: enige actie. Wanneer u de sjabloon uitvoert, geeft u deze een Go-structuur op met de gegevens die de tijdelijke aanduiding nodig heeft. 

Hier is een kort voorbeeld dat klopjacht maakt. Een knock-knock-grap heeft een zeer strikt format. De enige dingen die veranderen, zijn de identiteit van de klopper en de clou.

pakket main import ("tekst / sjabloon" "os") type Joke struct Die string Punchline string func main () t: = template.New ("Knock Knock Joke") tekst: = 'Knock Knock \ nWie is daar? .Wie wie wie? .Punchline 't.Prasse (tekst) grappen: = [] Joke "Etch", "Bless you!", "Cow goes", "No, cow goes moo!", For _, grap: = range grappen t.Execute (os.Stdout, joke) Uitgang: Knock Knock Wie is daar? Etch Etch wie? Gezondheid! Klop klop wie is daar? Koe gaat Koe gaat wie? Nee, koe gaat loeien!

Sjabloonacties begrijpen

De sjabloonsyntaxis is erg krachtig en ondersteunt acties zoals gegevenstoegang, functies, pijplijnen, variabelen, voorwaardelijke waarden en lussen.

Datatoegang

Datatoegang is heel eenvoudig. Ze trekken alleen gegevens uit de struct-start. Ze kunnen ook in geneste structs boren:

func main () family: = Family Father: Person "Tarzan", Mother: Person "Jane", ChildrenCount: 2, t: = template.New ("Father") text: = "The father's name is .Father.Name "t.Parse (text) t.Execute (os.Stdout, family) 

Als de gegevens geen struct zijn, kunt u gewoon gebruiken . om rechtstreeks toegang te krijgen tot de waarde:

func main () t: = template.New ("") t.Parse ("Anything goes: . \ n") t.Execute (os.Stdout, 1) t.Execute (os.Stdout, "twee") t.Execute (os.Stdout, 3.0) t.Execute (os.Stdout, map [string] int "four": 4) Uitgang: Anything goes: 1 Anything goes: two Anything goes: 3 Alles gaat: kaart [vier: 4] 

We zullen later zien hoe om te gaan met arrays, plakjes en kaarten.

functies

Functies verhogen echt wat u kunt doen met sjablonen. Er zijn veel algemene functies en u kunt zelfs sjabloonspecifieke functies toevoegen. De volledige lijst met algemene functies is beschikbaar op de Go-website.

Hier is een voorbeeld van hoe het te gebruiken printf functie in een sjabloon:

func main () t: = template.New ("") t.Parse ('Slechts 2 decimalen behouden van π: printf "% .2f".') t.Execute (os.Stdout, math. Pi) Uitvoer: behoud van slechts 2 decimalen van π: 3,14 

pijpleidingen

Met pijplijnen kunt u meerdere functies toepassen op de huidige waarde. Het combineren van verschillende functies vergroot de manier waarop je je waarden kunt verdelen en dobbelt aanzienlijk. 

In de volgende code keten ik drie functies. Eerst voert de aanroepfunctie de functie door naar Execute (). Dan de len functie retourneert de lengte van het resultaat van de invoerfunctie, die in dit geval 3 is. eindelijk, de printf functie drukt het aantal items af.

func main () t: = template.New ("") t.Parse ('call. | len | printf "% d items"') t.Execute (os.Stdout, func () string  return "abc") Uitgang: 3 items

Variabelen

Soms wilt u het resultaat van een complexe pijplijn meerdere keren opnieuw gebruiken. Met Go-sjablonen kunt u een variabele definiëren en deze zo vaak herhalen als u wilt. Het volgende voorbeeld haalt de voor- en achternaam uit de invoerstructuur, citeert ze en slaat ze op in de variabelen $ F en $ L. Daarna worden ze in normale en omgekeerde volgorde weergegeven. 

Nog een leuke truc hier is dat ik een anonieme structuur aan de sjabloon door geef om de code beknopter te maken en te voorkomen dat deze volproppen met typen die alleen op één plek worden gebruikt.

func main () t: = template.New ("") t.Parse ('$ F: = .FirstName | printf "% q" $ L: = .LastName | printf "% q"  Normaal: $ F $ L Achteruit: $ L $ F ') t.Execute (os.Stdout, struct FirstName string LastName string "Gigi "," Sayfan ",) Uitgang: Normaal:" Gigi "" Sayfan "Achteruit:" Sayfan "" Gigi "

Voorwaardelijke

Maar laten we hier niet stoppen. U kunt zelfs voorwaarden in uw sjablonen hebben. Er is een -end als actie en if-else-end actie. De if-clausule wordt weergegeven als de uitvoer van de voorwaardelijke pipeline niet leeg is:

func main () t: = template.New ("") t.Parse ('if. - . else Geen gegevens beschikbaar end') t. Execute (os.Stdout, "42") t.Execute (os.Stdout, "") Output: 42 Geen gegevens beschikbaar 

Merk op dat de else-component een nieuwe regel veroorzaakt en dat de tekst "Geen gegevens beschikbaar" aanzienlijk ingesprongen is.

Loops

Go-sjablonen hebben ook loops. Dit is super handig wanneer uw gegevens slices, kaarten of andere iterables bevatten. Het gegevensobject voor een lus kan elk willekeurig Go-object zijn zoals een array, segment, kaart of kanaal. Met de bereikfunctie kunt u het gegevensobject herhalen en een uitvoer voor elk element maken. Laten we eens kijken hoe u een kaart kunt herhalen:

func main () t: = template.New ("") e: = 'Naam, scores bereik $ k, $ v: =. $ k bereik $ 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,) Uitvoer: Name, Scores Betty, 54,96,78 Jake, 89,67,93 Mike, 88,77,99 

Zoals je kunt zien, is de leidende witruimte nog steeds een probleem. Ik kon geen fatsoenlijke manier vinden om dit aan te pakken binnen de sjabloonsyntaxis. Dit vereist nabewerking. In theorie kun je een streepje plaatsen om witruimte voorafgaand of na acties te trimmen, maar het werkt niet in de aanwezigheid van reeks.

Tekstsjablonen

Tekstsjablonen worden geïmplementeerd in het pakket met tekst / sjabloon. Naast alles wat we tot nu toe hebben gezien, kan dit pakket ook sjablonen laden uit bestanden en meerdere sjablonen samenstellen met behulp van de sjabloonactie. Het sjabloonobject zelf heeft veel methoden om dergelijke geavanceerde gebruikscasussen te ondersteunen:

  • ParseFiles ()
  • ParseGlob ()
  • AddParseTree ()
  • Clone ()
  • DefinedTemplates ()
  • Delims ()
  • ExecuteTemplate ()
  • Funcs ()
  • Opzoeken()
  • Keuze()
  • Templates ()

Vanwege ruimtebeperkingen zal ik niet verder ingaan (misschien in een andere tutorial).

HTML-sjablonen 

HTML-sjablonen worden gedefinieerd in het html / template-pakket. Het heeft precies dezelfde interface als het pakket met tekstsjablonen, maar het is ontworpen om HTML te genereren die veilig is voor code-injectie. Dit wordt gedaan door de gegevens zorgvuldig te ontsmetten voordat deze in de sjabloon worden ingesloten. De aanname is dat sjabloonauteurs vertrouwd zijn, maar de aan de sjabloon verstrekte gegevens kunnen niet worden vertrouwd. 

Dit is belangrijk. Als u automatisch sjablonen toepast die u van niet-vertrouwde bronnen ontvangt, beschermt het html / sjabloonpakket u niet. Het is uw verantwoordelijkheid om de sjablonen te controleren.

Laten we het verschil zien tussen de uitvoer van tekst / template en html / template. Wanneer u de tekst / sjabloon gebruikt, kunt u eenvoudig JavaScript-code in de gegenereerde uitvoer injecteren.

pakket main import ("tekst / sjabloon" "os") func main () t, _: = template.New (""). Ontleden ("Hallo, .!") d: = ""t.Execute (os.Stdout, d) Output: Hallo, ! 

Maar het importeren van de html / template in plaats van tekst / template voorkomt deze aanval door te ontsnappen aan de script-tags en tussen haakjes:

Hallo, !

Omgaan met fouten

Er zijn twee soorten fouten: parseerfouten en uitvoeringsfouten. De Ontleden () functie parseert de sjabloontekst en retourneert een fout, die ik heb genegeerd in de codevoorbeelden, maar in productiecode wil je deze fouten vroegtijdig opvangen en ze adresseren. 

Als je een snelle en vuile exit wilt, dan is de Moet() methode neemt de uitvoer van een methode die terugkeert (* Sjabloon, fout)-net zoals Clone (), Ontleden (), of ParseFiles ()-en paniek als de fout niet nul is. Hier ziet u hoe u controleert op een expliciete parseerfout:

func main () e: = "Ik ben een slechte sjabloon, " _, err: = template.New (""). Parse (e) if err! = nil msg: = "Is mislukt parsing: '% s'. \ nFout:% v \ n "fmt.Printf (msg, e, err) Uitvoer: kan sjabloon niet parseren: 'Ik ben een slechte sjabloon, '. Fout: sjabloon:: 1: onverwachte niet-gesloten actie in bevel 

Gebruik makend van Moet() alleen paniek als er iets mis is met de sjabloon:

func main () e: = "Ik ben een slechte sjabloon, " template.Must (template.New (""). Parse (e)) Uitvoer: paniek: sjabloon:: 1: onverwacht niet afgesloten actie in opdracht 

Het andere type fout is een uitvoeringsfout als de verstrekte gegevens niet overeenkomen met de sjabloon. Nogmaals, u kunt dit expliciet controleren of gebruiken Moet() in paniek raken. Ik raad u in dit geval aan om een ​​herstelmechanisme in te stellen en in te stellen. 

Meestal is het niet nodig om het hele systeem naar beneden te halen, alleen omdat een ingang niet voldoet aan de vereisten. In het volgende voorbeeld verwacht de sjabloon een veld met de naam Naam op de datastruct, maar ik geef een struct met een veld genaamd Voor-en achternaam.

func main () e: = "Er moet een naam zijn: .Name" t, _: = template.New (""). Parse (e) err: = t.Execute (os.Stdout, struct FullName string "Gigi Sayfan",) if err! = nil fmt.Println ("Fail to execute.", err) Output: Er moet een naam zijn: Fail to execute. template:: 1: 24: "" uitvoeren op <.Name>: veldnaam Naam in type struct FullName-reeks kan niet worden geëvalueerd 

Conclusie

Go heeft een krachtig en geavanceerd sjablonsysteem. Het is met veel succes gebruikt in veel grote projecten zoals Kubernetes en Hugo. Het html / template-pakket biedt een veilige, industriële krachtfaciliteit om de uitvoer van webgebaseerde systemen te ontsmetten. In deze zelfstudie hebben we alle basishandelingen en enkele gevallen van tussengebruik behandeld. 

Er zijn nog meer geavanceerde functies in de sjabloonpakketten die wachten om te worden ontgrendeld. Speel met sjablonen en neem ze op in uw programma's. U zult aangenaam verrast zijn hoe beknopt en leesbaar uw code voor het genereren van tekst eruitziet.