当前位置:网站首页>Standard library template learning introduction original

Standard library template learning introduction original

2022-06-12 18:53:00 Erya preaches Buddhism

html/template The package implements a data-driven template , Used to generate secure code that prevents code injection HTML Content . It provides and text/template Package the same interface ,Go Output in language HTML All scenarios should use html/template This package .

1, Template and rendering

In some cases, the front and rear ends are not separated Web Architecture , We usually need to render some data to HTML In the document , So as to realize dynamic web pages ( The layout and style of the web page are roughly the same , But the content of the display is different ) effect .

The template we are talking about here can be understood as pre-defined HTML Document file , The mechanism of template rendering can be simply understood as text replacement operation – Use the corresponding data to replace HTML Pre prepared marks in the document .

Many programming languages Web Various template engines are used in the framework , such as Python In language Flask Used in the framework jinja2 template engine .

1,Go Language template engine

Go The language has a built-in text template engine text/template And for HTML Document html/template. Their mechanism of action can be summarized as follows :

  1. Template files are usually defined as .tmpl and .tpl For the suffix ( Other suffixes can also be used ), You have to use UTF8 code .
  2. Use in template file {{ and }} Packages and identification need incoming data .
  3. Such data can be passed to the template through the point number (.) To visit , If the data is complex type data , Can pass { { .FieldName }} To access its fields .
  4. except {{ and }} Outside the contents of the package , Other contents will not be modified and output as is .

2, Use of template engine

Go The use of language template engine can be divided into three parts : Define template file 、 Parsing template files and template rendering .

1, Define template file

among , When defining the template file, we need to write it according to the relevant syntax rules , I'll give you a detailed introduction later .

2, Parse template file

After the template file is defined above , You can use the following common methods to parse the template file , Get the template object :

func (t *Template) Parse(src string) (*Template, error)
func ParseFiles(filenames ...string) (*Template, error)
func ParseGlob(pattern string) (*Template, error)

Of course , You can also use func New(name string) *Template Function to create a file named name The template of , Then call the above method to parse the template string or template file .

3, Template rendering

The rendering template is simply to fill the template with data , Of course, it may actually be a lot more complicated .

func (t *Template) Execute(wr io.Writer, data interface{}) error
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error

4, Basic example

1, Define template file

We are in accordance with the Go Template syntax defines a hello.tmpl Template file , The contents are as follows :

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello</title>
</head>
<body>
    <p>Hello {{.}}</p>
</body>
</html>
2, Parsing and rendering template files

And then we create one main.go file , Write down HTTP server The end code is as follows :

// main.go

func sayHello(w http.ResponseWriter, r *http.Request) {
	//  Parse the specified file to generate the template object 
	tmpl, err := template.ParseFiles("./hello.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	//  Render templates with given data , And write the result to w
	tmpl.Execute(w, " Shahe little prince ")
}
func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}

The above main.go File compilation execution , Then use a browser to access http://127.0.0.1:9090 You can see on the page “Hello Shahe little prince ”. This is an example of the simplest template rendering ,Go For detailed usage of the language template engine, please read on .

2, Template syntax

1,{{.}}

Template syntax is contained in {{ and }} middle , among {{.}} The points in represent the current object .

When we pass in a struct object , We can use . To access the corresponding fields of the structure . for example :

// main.go

package main

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

type User struct {
	Name   string
	Gender string
	Age    int
}

func sayHello(w http.ResponseWriter, r *http.Request) {
	// Define templates 
	// Analytical template 
	t, err := template.ParseFiles("./hello.tmpl")
	if err != nil {
		fmt.Println("parse file failed, err", err)
		return
	}
	// Apply colours to a drawing template 
	//u1 := User{
	//	Name:   "eryajf",
	//	Gender: " male ",
	//	Age:    10,
	//}
	// Words passed through the structure , Field names must be capitalized to access 
	// And by map In the form of , You do not need to capitalize , Because it is directly through key Access to the 
	m1 := map[string]interface{}{
		"Name":   " Xiao Ming ",
		"Gender": " male ",
		"Age":    20,
	}
	t.Execute(w, m1) //  The structure is passed here , perhaps map It's all right 
}

func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("Http server start failed, err", err)
		return
	}
}

Template file hello.tmpl The contents are as follows :

<html lang="zh=CN">
<head>
    <title>Hello</title>
</head>

<body>
<p> full name : {{ .Name }} </p>
<p> Gender : {{ .Gender }} </p>
<p> Age : {{ .Age }} </p>
</body>
</html>

Empathy , When the variable we pass in is map when , You can also use . according to key To take a value .

2, notes

{{/* a comment */}}
 notes , The execution ignores . You can do more . Comments cannot be nested , And it has to be close to the boundary .

3,pipeline

pipeline It refers to the operation of generating data . such as {{.}}{{.Name}} etc. .Go The use of pipeline symbols is supported in the template syntax of | Link multiple commands , Usage and unix The pipe under the pipe is similar :| The previous command changes the result of the operation ( Or return value ) The last position passed to the next command .

** Be careful :** It's not just the use of | It's just pipeline.Go In the template syntax of ,pipeline Of The concept is to transfer data , As long as you can generate data , All are pipeline.

4, Variable

We can also declare variables in the template , Used to save the data passed into the template or the results generated by other statements . The specific syntax is as follows :

$obj := {{.}}

among $obj It's the name of the variable , You can use this variable in subsequent code .

5, Remove spaces

Sometimes when we use template syntax, we will inevitably introduce spaces or line breaks , In this way, the final rendered content of the template may be different from what we think , It can be used at this time {{- Syntax removes all blank symbols to the left of the template content , Use -}} Remove all blank symbols to the right of the template content .

for example :

{{- .Name -}}

Be careful :- Be close to {{ and }}, At the same time, it needs to be separated from the template value with a space .

6, conditional

Go There are several kinds of conditional judgments in template syntax :

{{if pipeline}} T1 {{end}}

{{if pipeline}} T1 {{else}} T0 {{end}}

{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}

7,range

Go In the template syntax of range Keyword traversal , There are two ways of writing , among pipeline The value of must be an array 、 section 、 Dictionary or channel .

{{range pipeline}} T1 {{end}}
 If pipeline Its length is 0, There won't be any output 

{{range pipeline}} T1 {{else}} T0 {{end}}
 If pipeline Its length is 0, Will perform T0.

8,with

{{with pipeline}} T1 {{end}}
 If pipeline by empty No output , Otherwise it would be dot Set to pipeline And execute T1. Don't modify the outside dot.

{{with pipeline}} T1 {{else}} T0 {{end}}
 If pipeline by empty, Don't change dot And implement T0, otherwise dot Set to pipeline And execute T1.

9, Predefined functions

When executing the template , Function looks up... From two function dictionaries : The first is the template function dictionary , Then there's the global function dictionary . Generally, functions are not defined in templates , But use Funcs Method to add a function to the template .

The predefined global functions are as follows :

and
     Function returns its first empty Parameter or the last parameter ;
     That is to say "and x y" Equivalent to "if x then y else x"; All parameters are executed ;
or
     Return the first non empty Parameter or the last parameter ;
     or "or x y" Equivalent to "if x then x else y"; All parameters are executed ;
not
     Returns the negation of the Boolean value of its single argument 
len
     Returns the integer type length of its argument 
index
     The execution result is the first parameter, and the remaining parameters are the index / The value that the key points to ;
     Such as "index x 1 2 3" return x[1][2][3] Value ; Each indexed body must be an array 、 Slice or dictionary .
print
     namely fmt.Sprint
printf
     namely fmt.Sprintf
println
     namely fmt.Sprintln
html
     Returns an escape equivalent to the text representation of its argument HTML.
     This function is in the html/template Unavailable in .
urlquery
     Return the escape value of the text representation of its parameters in a form suitable for embedding in the URL query .
     This function is in the html/template Unavailable in .
js
     Returns an escape equivalent to the text representation of its argument JavaScript.
call
     The result of execution is the return value of the first parameter , The argument must be of function type , The remaining parameters are used as the parameters to call the function ;
     Such as "call .X.Y 1 2" Equivalent to go In the language dot.X.Y(1, 2);
     among Y Is the value of a function type field or dictionary , Or something like that ;
    call The execution result of the first parameter of must be the value of the function type ( And predefined functions like print Obviously different );
     The function type value must have 1 To 2 Return values , If there is 2 The latter must be error Interface type ;
     If there is 2 Methods that return values return error Not nil, Template execution is interrupted and returned to the calling template executor ;

10, Comparison function

Boolean functions treat any type of zero as false , The rest is considered true .

Here is the set of binary comparison operations defined as functions :

eq       If arg1 == arg2 Then return to true 
ne       If arg1 != arg2 Then return to true 
lt       If arg1 < arg2 Then return to true 
le       If arg1 <= arg2 Then return to true 
gt       If arg1 > arg2 Then return to true 
ge       If arg1 >= arg2 Then return to true 

In order to simplify the multi parameter equality detection ,eq( Only eq) Acceptable 2 One or more parameters , It compares the first parameter with the rest in turn , Return the result of the following formula :

{{eq arg1 arg2 arg3}}

Comparison functions only apply to basic types ( Or redefined basic types , Such as ”type Celsius float32”). however , Integers and floating-point numbers cannot be compared with each other .

11, Custom function

Go The template supports custom functions .

main.go

package main

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

func f1(w http.ResponseWriter, r *http.Request) {
	//  Define a function  kua
	k := func(name string) (string, error) {
		return name + " Young and handsome !", nil
	}

	t := template.New("f.tmpl") //  Create a template object , The template name should correspond to the template file parsed below 
	//  Tell the template engine , I now define a kua This function , This step should be done before parsing the template 
	t.Funcs(template.FuncMap{
		"kua99": k,
	})
	_, err := t.ParseFiles("f.tmpl")
	if err != nil {
		fmt.Printf("parse template file failed, err:%v\n", err)
		return
	}
	name := "eryajf"
	//  Apply colours to a drawing template 
	t.Execute(w, name)
}

func demo1(w http.ResponseWriter, r *http.Request) {
	// Analytical template 
	t, err := template.ParseFiles("t.tmpl", "ul.tmpl") // Be careful : Nested templates should be placed behind 
	if err != nil {
		fmt.Printf("parse template failed, err:%v\n", err)
		return
	}
	// Apply colours to a drawing template 
	name := " Erya speaks Buddhism "
	t.Execute(w, name)
}

func main() {
	http.HandleFunc("/", f1)
	http.HandleFunc("/tmpl", demo1)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("Http server start failed, err", err)
		return
	}
}

f.tmpl

<html lang="zh=CN">
<head>
    <title> Custom function </title>
</head>

<body>
<p> {{ kua99 . }} </p>
</body>
</html>

t.tmpl

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>tmpl test</title>
</head>
<body>

<h1> Test nesting template grammar </h1>
<hr>
{{/* Nested another separate template file */}}
{{template "ul.tmpl"}}
<hr>
{{/* Nested a pass define Imported template file */}}
{{template "ol.tmpl"}}
</body>

<p>  Hello  {{  . }} </p>
</html>

{{ define "ol.tmpl"}}
    <ol>
        <li> having dinner </li>
        <li> sleep </li>
        <li> Doudou </li>
    </ol>
{{end}}

ul.tmpl

<ul>
    <li> notes </li>
    <li> journal </li>
    <li> test </li>
</ul>

We can do it in the template file hello.tmpl Use our custom in the following way kua Function .

{{kua .Name}}

12, nesting template

We can do it in template Nested other template. This template It can be a separate file , Or through define Defined template.

for instance : t.tmpl The contents of the document are as follows :

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>tmpl test</title>
</head>
<body>
    
    <h1> Test nesting template grammar </h1>
    <hr>
    {{template "ul.tmpl"}}
    <hr>
    {{template "ol.tmpl"}}
</body>
</html>

{{ define "ol.tmpl"}}
<ol>
    <li> having dinner </li>
    <li> sleep </li>
    <li> Doudou </li>
</ol>
{{end}}

ul.tmpl The contents of the document are as follows :

<ul>
    <li> notes </li>
    <li> journal </li>
    <li> test </li>
</ul>

Let's sign up for templDemo Routing functions .

http.HandleFunc("/tmpl", tmplDemo)

tmplDemo The specific content of the function is as follows :

func tmplDemo(w http.ResponseWriter, r *http.Request) {
	tmpl, err := template.ParseFiles("./t.tmpl", "./ul.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	user := UserInfo{
		Name:   " The little prince ",
		Gender: " male ",
		Age:    18,
	}
	tmpl.Execute(w, user)
}

Be careful : When parsing a template , Nested templates must be parsed later , For example, in the example above t.tmpl Nested in the template ul.tmpl, therefore ul.tmpl To be in t.tmpl After the analysis .

13,block

{{block "name" pipeline}} T1 {{end}}

block Is to define a template {{define "name"}} T1 {{end}} And execution {{template "name" pipeline}} abbreviation , A typical usage is to define a set of root templates , Then customize it by redefining the block template .

Define a root template templates/base.tmpl, The contents are as follows :

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Go Templates</title>
</head>
<body>
<div class="container-fluid">
    {{block "content" . }}{{end}}
</div>
</body>
</html>

Then define a templates/index.tmpl,” Inherit ”base.tmpl

{{template "base.tmpl"}}

{{define "content"}}
    <div>Hello world!</div>
{{end}}

And then use template.ParseGlob Parse template files according to regular matching rules , And then through ExecuteTemplate Render the specified template :

func index(w http.ResponseWriter, r *http.Request){
	tmpl, err := template.ParseGlob("templates/*.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	err = tmpl.ExecuteTemplate(w, "index.tmpl", nil)
	if err != nil {
		fmt.Println("render template failed, err:", err)
		return
	}
}

If our template names conflict , For example, there is a..., defined under different business lines index.tmpl Templates , We can solve this problem in the following two ways .

  1. Use... At the beginning of the template file {{define Template name }} Statement to explicitly name the template .
  2. You can store the template file in templates In different directories under the folder , And then use template.ParseGlob("templates/**/*.tmpl") Analytical template .

14, Modify the default identifier

Go Curly braces used by the template engine of the standard library {{ and }} As identification , And many front-end frameworks ( Such as Vue and AngularJS) Also used {{ and }} As identifier , So when we use both Go There will be a conflict between the language template engine and the above front-end framework , At this time, we need to modify the identifier , Modify the front-end or modify Go Linguistic . Here's how to modify Go The default identifier of the language template engine :

template.New("test").Delims("{[", "]}").ParseFiles("./t.tmpl")

3,text/template And html/tempalte The difference between

html/template It is aimed at the need to return HTML Content scene , Some risky content will be escaped during template rendering , In order to prevent cross site scripting attacks .

for example , I define the following template file :

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello</title>
</head>
<body>
{{.str1}}
{{.str2 | safe }}
</body>
</html>

At this time, a period of JS Code and use html/template To render the file , The escaped... Will be displayed on the page JS Content . alert(' Hey, hey, hey ') This is it. html/template What you did for us .

But in some scenarios , If we believe what the user enters , If you don't want to escape , You can write a safe function , Manually return to a template.HTML Type of content . Examples are as follows :

package main

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

func index(w http.ResponseWriter, r *http.Request) {
	t, err := template.New("index.tmpl").Funcs(template.FuncMap{
		"safe": func(s string) template.HTML {
			return template.HTML(s)
		},
	}).ParseFiles("./index.tmpl")
	//t, err := template.ParseFiles("./index.tmpl")
	if err != nil {
		fmt.Printf("parse template failed, err:%v\n", err)
		return
	}
	str1 := "<script>alert(123);</script>"
	str2 := "<a href='http://eryajf.net'> Erya's blog about fan </a>"

	t.Execute(w, map[string]string{
		"str1": str1,
		"str2": str2,
	})
}

func main() {
	http.HandleFunc("/index", index)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("Http server start failed, err", err)
		return
	}
}

In this way, we only need to use our defined after the content of the template file that does not need to be escaped safe Function is OK .

{{ . | safe }}

4, Load static file

First create two static files , Simulation project structure .

templates/posts/index.tmpl

{{define "posts/index.tmpl"}}
    <!DOCTYPE html>
    <html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>posts/index</title>
    </head>
    <body>
    {{ .title }}
    </body>
    </html>
{{end}}

templates/posts/index.tmpl

{{define "users/index.tmpl"}}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="/xxx/index.css">
        <title>users/index</title>
    </head>
    <body>
    {{ .title | safe }}
    </body>
    <script src="/xxx/index.js"></script>
    </html>
{{end}}

Then you can uniformly quote , One r.LoadHTMLGlob("templates/**/*"), One r.Static("xxx", "static")

package main

import (
	"github.com/gin-gonic/gin"
	"html/template"
	"net/http"
)

func main() {
	r := gin.Default()
	//  Load static file 
	r.Static("xxx", "static")
	// gin  Add custom functions to the template in the framework 
	r.SetFuncMap(template.FuncMap{
		"safe": func(str string) template.HTML {
			return template.HTML(str)
		},
	})
	//r.LoadHTMLFiles("templates/index.tmpl") // Template parsing 
	r.LoadHTMLGlob("templates/**/*")

	r.GET("/posts/index", func(c *gin.Context) {
		// HTTP request 
		c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ // Template rendering 
			"title": "posts page",
		})
	})

	r.GET("/users/index", func(c *gin.Context) {
		// HTTP request 
		c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ // Template rendering 
			"title": "<a href='http://eryajf.net'> Erya's blog about fan </a>",
		})
	})

	r.GET("home", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})

	r.Run(":9090") // start-up server

}
原网站

版权声明
本文为[Erya preaches Buddhism]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/163/202206121826198107.html