From 74268581ba82a7384690f88559ae5d904e2abbba Mon Sep 17 00:00:00 2001 From: vCaesar Date: Sat, 10 Jun 2017 12:01:02 +0800 Subject: [PATCH] Format and remove 07.4.md spaces --- zh/07.4.md | 414 ++++++++++++++++++++++++++--------------------------- 1 file changed, 207 insertions(+), 207 deletions(-) diff --git a/zh/07.4.md b/zh/07.4.md index 48db045a..f5b54bb5 100644 --- a/zh/07.4.md +++ b/zh/07.4.md @@ -14,12 +14,12 @@ Web应用反馈给客户端的信息中的大部分内容是静态的,不变 在Go语言中,我们使用`template`包来进行模板处理,使用类似`Parse`、`ParseFile`、`Execute`等方法从文件或者字符串加载模板,然后执行类似上面图片展示的模板的merge操作。请看下面的例子: ```Go - func handler(w http.ResponseWriter, r *http.Request) { - t := template.New("some template") //创建一个模板 - t, _ = t.ParseFiles("tmpl/welcome.html", nil) //解析模板文件 - user := GetUser() //获取当前用户信息 - t.Execute(w, user) //执行模板的merger操作 - } +func handler(w http.ResponseWriter, r *http.Request) { + t := template.New("some template") //创建一个模板 + t, _ = t.ParseFiles("tmpl/welcome.html", nil) //解析模板文件 + user := GetUser() //获取当前用户信息 + t.Execute(w, user) //执行模板的merger操作 +} ``` 通过上面的例子我们可以看到Go语言的模板操作非常的简单方便,和其他语言的模板处理类似,都是先获取数据,然后渲染数据。 @@ -36,33 +36,33 @@ Web应用反馈给客户端的信息中的大部分内容是静态的,不变 Go语言的模板通过`{{}}`来包含需要在渲染时被替换的字段,`{{.}}`表示当前的对象,这和Java或者C++中的this类似,如果要访问当前对象的字段通过`{{.FieldName}}`,但是需要注意一点:这个字段必须是导出的(字段首字母必须是大写的),否则在渲染的时候就会报错,请看下面的这个例子: ```Go - package main +package main - import ( - "html/template" - "os" - ) +import ( + "html/template" + "os" +) - type Person struct { - UserName string - } +type Person struct { + UserName string +} - func main() { - t := template.New("fieldname example") - t, _ = t.Parse("hello {{.UserName}}!") - p := Person{UserName: "Astaxie"} - t.Execute(os.Stdout, p) - } +func main() { + t := template.New("fieldname example") + t, _ = t.Parse("hello {{.UserName}}!") + p := Person{UserName: "Astaxie"} + t.Execute(os.Stdout, p) +} ``` 上面的代码我们可以正确的输出`hello Astaxie`,但是如果我们稍微修改一下代码,在模板中含有了未导出的字段,那么就会报错 ```Go - type Person struct { - UserName string - email string //未导出的字段,首字母是小写的 - } +type Person struct { + UserName string + email string //未导出的字段,首字母是小写的 +} - t, _ = t.Parse("hello {{.UserName}}! {{.email}}") +t, _ = t.Parse("hello {{.UserName}}! {{.email}}") ``` 上面的代码就会报错,因为我们调用了一个未导出的字段,但是如果我们调用了一个不存在的字段是不会报错的,而是输出为空。 @@ -77,67 +77,67 @@ Go语言的模板通过`{{}}`来包含需要在渲染时被替换的字段,`{{ 详细的使用请看下面的例子: ```Go - package main +package main - import ( - "html/template" - "os" - ) +import ( + "html/template" + "os" +) - type Friend struct { - Fname string - } +type Friend struct { + Fname string +} - type Person struct { - UserName string - Emails []string - Friends []*Friend - } +type Person struct { + UserName string + Emails []string + Friends []*Friend +} - func main() { - f1 := Friend{Fname: "minux.ma"} - f2 := Friend{Fname: "xushiwei"} - t := template.New("fieldname example") - t, _ = t.Parse(`hello {{.UserName}}! - {{range .Emails}} - an email {{.}} - {{end}} - {{with .Friends}} - {{range .}} - my friend name is {{.Fname}} - {{end}} - {{end}} - `) - p := Person{UserName: "Astaxie", - Emails: []string{"astaxie@beego.me", "astaxie@gmail.com"}, - Friends: []*Friend{&f1, &f2}} - t.Execute(os.Stdout, p) - } +func main() { + f1 := Friend{Fname: "minux.ma"} + f2 := Friend{Fname: "xushiwei"} + t := template.New("fieldname example") + t, _ = t.Parse(`hello {{.UserName}}! + {{range .Emails}} + an email {{.}} + {{end}} + {{with .Friends}} + {{range .}} + my friend name is {{.Fname}} + {{end}} + {{end}} + `) + p := Person{UserName: "Astaxie", + Emails: []string{"astaxie@beego.me", "astaxie@gmail.com"}, + Friends: []*Friend{&f1, &f2}} + t.Execute(os.Stdout, p) +} ``` ### 条件处理 在Go模板里面如果需要进行条件判断,那么我们可以使用和Go语言的`if-else`语法类似的方式来处理,如果pipeline为空,那么if就认为是false,下面的例子展示了如何使用`if-else`语法: ```Go - package main +package main - import ( - "os" - "text/template" - ) +import ( + "os" + "text/template" +) - func main() { - tEmpty := template.New("template test") - tEmpty = template.Must(tEmpty.Parse("空 pipeline if demo: {{if ``}} 不会输出. {{end}}\n")) - tEmpty.Execute(os.Stdout, nil) +func main() { + tEmpty := template.New("template test") + tEmpty = template.Must(tEmpty.Parse("空 pipeline if demo: {{if ``}} 不会输出. {{end}}\n")) + tEmpty.Execute(os.Stdout, nil) - tWithValue := template.New("template test") - tWithValue = template.Must(tWithValue.Parse("不为空的 pipeline if demo: {{if `anything`}} 我有内容,我会输出. {{end}}\n")) - tWithValue.Execute(os.Stdout, nil) + tWithValue := template.New("template test") + tWithValue = template.Must(tWithValue.Parse("不为空的 pipeline if demo: {{if `anything`}} 我有内容,我会输出. {{end}}\n")) + tWithValue.Execute(os.Stdout, nil) - tIfElse := template.New("template test") - tIfElse = template.Must(tIfElse.Parse("if-else demo: {{if `anything`}} if部分 {{else}} else部分.{{end}}\n")) - tIfElse.Execute(os.Stdout, nil) - } + tIfElse := template.New("template test") + tIfElse = template.Must(tIfElse.Parse("if-else demo: {{if `anything`}} if部分 {{else}} else部分.{{end}}\n")) + tIfElse.Execute(os.Stdout, nil) +} ``` 通过上面的演示代码我们知道`if-else`语法相当的简单,在使用过程中很容易集成到我们的模板代码中。 @@ -147,8 +147,8 @@ Go语言的模板通过`{{}}`来包含需要在渲染时被替换的字段,`{{ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "beego"`类似这样的语法你是不是经常使用,过滤当前目录下面的文件,显示含有"beego"的数据,表达的意思就是前面的输出可以当做后面的输入,最后显示我们想要的数据,而Go语言模板最强大的一点就是支持pipe数据,在Go语言里面任何`{{}}`里面的都是pipelines数据,例如我们上面输出的email里面如果还有一些可能引起XSS注入的,那么我们如何来进行转化呢? ```Go - {{. | html}} - +{{. | html}} + ``` 在email输出的地方我们可以采用如上方式可以把输出全部转化html的实体,上面的这种方式和我们平常写Unix的方式是不是一模一样,操作起来相当的简便,调用其他的函数也是类似的方式。 @@ -156,14 +156,14 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "beego"`类似这样的 有时候,我们在模板使用过程中需要定义一些局部变量,我们可以在一些操作中申明局部变量,例如`with``range``if`过程中申明局部变量,这个变量的作用域是`{{end}}`之前,Go语言通过申明的局部变量格式如下所示: ```Go - $variable := pipeline +$variable := pipeline ``` 详细的例子看下面的: ```Go - {{with $x := "output" | printf "%q"}}{{$x}}{{end}} - {{with $x := "output"}}{{printf "%q" $x}}{{end}} - {{with $x := "output"}}{{$x | printf "%q"}}{{end}} +{{with $x := "output" | printf "%q"}}{{$x}}{{end}} +{{with $x := "output"}}{{printf "%q" $x}}{{end}} +{{with $x := "output"}}{{$x | printf "%q"}}{{end}} ``` ### 模板函数 @@ -172,194 +172,194 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "beego"`类似这样的 每一个模板函数都有一个唯一值的名字,然后与一个Go函数关联,通过如下的方式来关联 ```Go - type FuncMap map[string]interface{} +type FuncMap map[string]interface{} ``` 例如,如果我们想要的email函数的模板函数名是`emailDeal`,它关联的Go函数名称是`EmailDealWith`,那么我们可以通过下面的方式来注册这个函数 ```Go - t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith}) +t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith}) ``` `EmailDealWith`这个函数的参数和返回值定义如下: ```Go - func EmailDealWith(args …interface{}) string +func EmailDealWith(args …interface{}) string ``` 我们来看下面的实现例子: ```Go - package main +package main - import ( - "fmt" - "html/template" - "os" - "strings" - ) +import ( + "fmt" + "html/template" + "os" + "strings" +) - type Friend struct { - Fname string +type Friend struct { + Fname string +} + +type Person struct { + UserName string + Emails []string + Friends []*Friend +} + +func EmailDealWith(args ...interface{}) string { + ok := false + var s string + if len(args) == 1 { + s, ok = args[0].(string) } - - type Person struct { - UserName string - Emails []string - Friends []*Friend + if !ok { + s = fmt.Sprint(args...) } - - func EmailDealWith(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - // find the @ symbol - substrs := strings.Split(s, "@") - if len(substrs) != 2 { - return s - } - // replace the @ by " at " - return (substrs[0] + " at " + substrs[1]) + // find the @ symbol + substrs := strings.Split(s, "@") + if len(substrs) != 2 { + return s } + // replace the @ by " at " + return (substrs[0] + " at " + substrs[1]) +} - func main() { - f1 := Friend{Fname: "minux.ma"} - f2 := Friend{Fname: "xushiwei"} - t := template.New("fieldname example") - t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith}) - t, _ = t.Parse(`hello {{.UserName}}! - {{range .Emails}} - an emails {{.|emailDeal}} - {{end}} - {{with .Friends}} - {{range .}} - my friend name is {{.Fname}} - {{end}} - {{end}} - `) - p := Person{UserName: "Astaxie", - Emails: []string{"astaxie@beego.me", "astaxie@gmail.com"}, - Friends: []*Friend{&f1, &f2}} - t.Execute(os.Stdout, p) - } +func main() { + f1 := Friend{Fname: "minux.ma"} + f2 := Friend{Fname: "xushiwei"} + t := template.New("fieldname example") + t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith}) + t, _ = t.Parse(`hello {{.UserName}}! + {{range .Emails}} + an emails {{.|emailDeal}} + {{end}} + {{with .Friends}} + {{range .}} + my friend name is {{.Fname}} + {{end}} + {{end}} + `) + p := Person{UserName: "Astaxie", + Emails: []string{"astaxie@beego.me", "astaxie@gmail.com"}, + Friends: []*Friend{&f1, &f2}} + t.Execute(os.Stdout, p) +} ``` 上面演示了如何自定义函数,其实,在模板包内部已经有内置的实现函数,下面代码截取自模板包里面 ```Go - var builtins = FuncMap{ - "and": and, - "call": call, - "html": HTMLEscaper, - "index": index, - "js": JSEscaper, - "len": length, - "not": not, - "or": or, - "print": fmt.Sprint, - "printf": fmt.Sprintf, - "println": fmt.Sprintln, - "urlquery": URLQueryEscaper, - } +var builtins = FuncMap{ + "and": and, + "call": call, + "html": HTMLEscaper, + "index": index, + "js": JSEscaper, + "len": length, + "not": not, + "or": or, + "print": fmt.Sprint, + "printf": fmt.Sprintf, + "println": fmt.Sprintln, + "urlquery": URLQueryEscaper, +} ``` ## Must操作 模板包里面有一个函数`Must`,它的作用是检测模板是否正确,例如大括号是否匹配,注释是否正确的关闭,变量是否正确的书写。接下来我们演示一个例子,用Must来判断模板是否正确: ```Go - package main +package main - import ( - "fmt" - "text/template" - ) +import ( + "fmt" + "text/template" +) - func main() { - tOk := template.New("first") - template.Must(tOk.Parse(" some static text /* and a comment */")) - fmt.Println("The first one parsed OK.") +func main() { + tOk := template.New("first") + template.Must(tOk.Parse(" some static text /* and a comment */")) + fmt.Println("The first one parsed OK.") - template.Must(template.New("second").Parse("some static text {{ .Name }}")) - fmt.Println("The second one parsed OK.") + template.Must(template.New("second").Parse("some static text {{ .Name }}")) + fmt.Println("The second one parsed OK.") - fmt.Println("The next one ought to fail.") - tErr := template.New("check parse error with Must") - template.Must(tErr.Parse(" some static text {{ .Name }")) - } + fmt.Println("The next one ought to fail.") + tErr := template.New("check parse error with Must") + template.Must(tErr.Parse(" some static text {{ .Name }")) +} ``` 将输出如下内容 ``` - The first one parsed OK. - The second one parsed OK. - The next one ought to fail. - panic: template: check parse error with Must:1: unexpected "}" in command +The first one parsed OK. +The second one parsed OK. +The next one ought to fail. +panic: template: check parse error with Must:1: unexpected "}" in command ``` ## 嵌套模板 我们平常开发Web应用的时候,经常会遇到一些模板有些部分是固定不变的,然后可以抽取出来作为一个独立的部分,例如一个博客的头部和尾部是不变的,而唯一改变的是中间的内容部分。所以我们可以定义成`header`、`content`、`footer`三个部分。Go语言中通过如下的语法来申明 ```Go - {{define "子模板名称"}}内容{{end}} +{{define "子模板名称"}}内容{{end}} ``` 通过如下方式来调用: ```Go - {{template "子模板名称"}} +{{template "子模板名称"}} ``` 接下来我们演示如何使用嵌套模板,我们定义三个文件,`header.tmpl`、`content.tmpl`、`footer.tmpl`文件,里面的内容如下 ```html - //header.tmpl - {{define "header"}} - - - 演示信息 - - - {{end}} +//header.tmpl +{{define "header"}} + + + 演示信息 + + +{{end}} - //content.tmpl - {{define "content"}} - {{template "header"}} -

演示嵌套

- - {{template "footer"}} - {{end}} +//content.tmpl +{{define "content"}} +{{template "header"}} +

演示嵌套

+ +{{template "footer"}} +{{end}} - //footer.tmpl - {{define "footer"}} - - - {{end}} +//footer.tmpl +{{define "footer"}} + + +{{end}} ``` 演示代码如下: ```Go - package main +package main - import ( - "fmt" - "os" - "text/template" - ) +import ( + "fmt" + "os" + "text/template" +) - func main() { - s1, _ := template.ParseFiles("header.tmpl", "content.tmpl", "footer.tmpl") - s1.ExecuteTemplate(os.Stdout, "header", nil) - fmt.Println() - s1.ExecuteTemplate(os.Stdout, "content", nil) - fmt.Println() - s1.ExecuteTemplate(os.Stdout, "footer", nil) - fmt.Println() - s1.Execute(os.Stdout, nil) - } +func main() { + s1, _ := template.ParseFiles("header.tmpl", "content.tmpl", "footer.tmpl") + s1.ExecuteTemplate(os.Stdout, "header", nil) + fmt.Println() + s1.ExecuteTemplate(os.Stdout, "content", nil) + fmt.Println() + s1.ExecuteTemplate(os.Stdout, "footer", nil) + fmt.Println() + s1.Execute(os.Stdout, nil) +} ``` 通过上面的例子我们可以看到通过`template.ParseFiles`把所有的嵌套模板全部解析到模板里面,其实每一个定义的{{define}}都是一个独立的模板,他们相互独立,是并行存在的关系,内部其实存储的是类似map的一种关系(key是模板的名称,value是模板的内容),然后我们通过`ExecuteTemplate`来执行相应的子模板内容,我们可以看到header、footer都是相对独立的,都能输出内容,content 中因为嵌套了header和footer的内容,就会同时输出三个的内容。但是当我们执行`s1.Execute`,没有任何的输出,因为在默认的情况下没有默认的子模板,所以不会输出任何的东西。