自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Goctl 技術(shù)系列 - 通過(guò)模板簡(jiǎn)化應(yīng)用開(kāi)發(fā)

開(kāi)發(fā) 前端
本文介紹了 Go 語(yǔ)言中 text/template? 包的基礎(chǔ)功能、中級(jí)功能和高級(jí)功能,并通過(guò)具體示例講解了每個(gè)功能的使用方法。

在現(xiàn)代軟件開(kāi)發(fā)中,數(shù)據(jù)驅(qū)動(dòng)的應(yīng)用程序逐漸成為主流。無(wú)論是構(gòu)建動(dòng)態(tài)網(wǎng)站、代碼生成,生成配置文件,還是創(chuàng)建復(fù)雜的文檔模板,數(shù)據(jù)驅(qū)動(dòng)的方式都能顯著提升開(kāi)發(fā)效率和代碼可維護(hù)性。在 Go 語(yǔ)言中,text/template 包提供了一種強(qiáng)大的方式來(lái)處理文本和數(shù)據(jù)的結(jié)合。

一、基礎(chǔ)功能回顧

文本和空格

在 text/template 中,文本和空格的處理直接影響到最終輸出的格式。模板中的文本會(huì)原樣輸出,而空格和換行符會(huì)保留。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `Hello, {{.Name}}!
Welcome to the Go template tutorial.`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Name string
    }{
       Name: "go-zero",
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:在這個(gè)示例中,模板中的文本會(huì)被原樣輸出,包括空格和換行符。模板中的 {{.Name}} 會(huì)被替換為數(shù)據(jù)中的 Name 字段。

動(dòng)作

注釋 action

注釋可以在模板中添加不輸出的文本,使用 {{/* ... */}} 語(yǔ)法。

注意:/*后和*/前必須有一個(gè)空格。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `Hello, {{.Name}}! {{/* This is a comment */}}`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Name string
    }{
       Name: "go-zero",
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:注釋內(nèi)容不會(huì)出現(xiàn)在最終輸出中,可以用于在模板中添加說(shuō)明或備注。

if action

if action 用于條件判斷,如果條件為真,則輸出其中的內(nèi)容。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `{{if .ShowTitle}}<h1>{{.Title}}</h1>{{end}}`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       ShowTitle bool
       Title     string
    }{
       ShowTitle: true,
       Title:     "Hello, go-zero!",
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:if action 檢查 ShowTitle 是否為 true,如果是,則輸出標(biāo)題。

if-else action

if-else action 用于條件判斷,如果條件為真,則輸出 if 部分的內(nèi)容,否則輸出 else 部分的內(nèi)容。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `{{if .ShowTitle}}<h1>{{.Title}}</h1>{{else}}<h1>No Title</h1>{{end}}`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       ShowTitle bool
       Title     string
    }{
       ShowTitle: false,
       Title:     "Hello, go-zero!",
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:if-else action 檢查 ShowTitle 是否為 true,如果是,則輸出標(biāo)題,否則輸出 No Title。

if-else-if action

if-else-if action 用于多個(gè)條件的判斷。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `{{if .ShowTitle}}<h1>{{.Title}}</h1>{{else if .ShowSubtitle}}<h2>{{.Subtitle}}</h2>{{else}}<p>No Title or Subtitle</p>{{end}}`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       ShowTitle    bool
       ShowSubtitle bool
       Title        string
       Subtitle     string
    }{
       ShowTitle:    false,
       ShowSubtitle: true,
       Title:        "Hello, go-zero!",
       Subtitle:     "Hi, go-zero!",
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:if-else-if action 檢查 ShowTitle 是否為 true,如果是,則輸出標(biāo)題<h1>{{Hello, go-zero!}}</h1>;否則檢查 ShowSubtitle 是否為 true,如果是,則輸出副標(biāo)題;否則輸出 <h2>Hi, go-zero!</h2>。

字段鏈?zhǔn)秸{(diào)用

字段鏈?zhǔn)秸{(diào)用用于訪問(wèn)嵌套的結(jié)構(gòu)體字段。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `Name: {{.Repo.Name}}, Address: {{.Repo.Address}}`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Repo struct {
          Name    string
          Address string
       }
    }{
       Repo: struct {
          Name    string
          Address string
       }{
          Name:    "go-zero",
          Address: "https://github.com/zeromicro/go-zero",
       },
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:字段鏈?zhǔn)秸{(diào)用通過(guò) . 操作符訪問(wèn)嵌套結(jié)構(gòu)體中的字段,例如 {{.Repo.Name}} 和 {{.Repo.Address}}。

二、中級(jí)功能

range 數(shù)組

range action 用于遍歷數(shù)組或切片。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `
<ul>
{{range .Projects}}<li>{{.}}</li>{{end}}
</ul>
`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Projects []string
    }{
       Projects: []string{"go-zero", "goctl"},
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:range action 遍歷 Projects 切片中的每個(gè)元素,并生成一個(gè)包含每個(gè)元素的列表項(xiàng) (<li>)。

range map

range action 也可以用于遍歷 map。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `
<ul>
{{range $key, $value := .Projects}}<li>{{$key}}: {{$value}}</li>{{end}}
</ul>
`
    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Projects map[string]string
    }{
       Projects: map[string]string{"Name": "go-zero", "Address": "https://github.com/zeromicro/go-zero"},
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:range map action 遍歷 Projects 切片中的每個(gè)key, value 元素,其變量以 $ 開(kāi)頭。

break action

在range動(dòng)作中可以通過(guò) break 來(lái)中斷循環(huán)。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `
<ul>{{range .Items}}
  {{if eq . "Item 3"}}{{break}}{{end}}<li>{{.}}</li>{{end}}
</ul>`

    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Items []string
    }{
       Items: []string{"Item 1", "Item 2", "Item 3"},
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:通過(guò){{break}},可以打斷range循環(huán)操作,如上結(jié)果輸出為

<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  
</ul>

continue action

在range動(dòng)作中可以通過(guò) continue 來(lái)跳過(guò)當(dāng)次循環(huán)。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `<ul>{{range .Items}}
  {{if eq . "Item 2"}}{{continue}}{{else}}<li>{{.}}</li>{{end}}{{end}}
</ul>`

    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Items []string
    }{
       Items: []string{"Item 1", "Item 2", "Item 3"},
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
       panic(err)
    }
}

講解:通過(guò) continue 動(dòng)作,在模板中遇到特定條件時(shí)可以跳過(guò)當(dāng)前循環(huán)。以上模板輸出為

<ul>
  <li>Item 1</li>
  
  <li>Item 3</li>
</ul>

子模板

子模板允許將模板劃分為多個(gè)部分,并在主模板中引用子模板。

package main

import (
    "os"
    "text/template"
)

func main() {
    const (
       headerTemplate = `{{define "header"}}<html><head><title>{{.Title}}</title></head><body>{{end}}`
       footerTemplate = `{{define "footer"}}</body></html>{{end}}`
       bodyTemplate   = `{{define "body"}}<h1>{{.Heading}}</h1><p>{{.Content}}</p>{{end}}`
       mainTemplate   = `{{template "header" .}} {{template "body" .}} {{template "footer" .}}`
    )

    tmpl := template.Must(template.New("main").Parse(headerTemplate + footerTemplate + bodyTemplate + mainTemplate))

    data := struct {
       Title   string
       Heading string
       Content string
    }{
       Title:   "Welcome",
       Heading: "Hello, go-zero!",
       Content: "This is a simple example of nested templates.",
    }

    if err := tmpl.Execute(os.Stdout, data); err != nil {
       panic(err)
    }
}

講解:通過(guò)定義 header、footer 和 body 子模板,并在主模板中使用 {{template "header" .}} 等方式引用子模板,可以實(shí)現(xiàn)模板的模塊化和復(fù)用。

with action

with action 設(shè)置一個(gè)新的數(shù)據(jù)上下文,并在該上下文中執(zhí)行模板。

package main

import (
    "os"
    "text/template"
)

func main() {
    const templateText = `{{with .User}}<p>Name: {{.Name}}</p>
<p>Age: {{.Age}}</p>
{{end}}
`

    tmpl, err := template.New("example").Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       User struct {
          Name string
          Age  int
       }
    }{
       User: struct {
          Name string
          Age  int
       }{
          Name: "John",
          Age:  30,
       },
    }

    if err := tmpl.Execute(os.Stdout, data); err != nil {
       panic(err)
    }
}

講解:with action 將 User 結(jié)構(gòu)體設(shè)置為新的數(shù)據(jù)上下文{{.}},從而簡(jiǎn)化了模板中對(duì)嵌套字段的訪問(wèn)。

三、高級(jí)功能

內(nèi)置函數(shù)

Go 模板提供了一些常用的內(nèi)置函數(shù),可以直接在模板中使用。

package main

import (
        "os"
        "text/template"
)

func main() {
        const templateText = `
<p>Upper: {{.Name | upper}}</p>
<p>Len: {{len .Name}}</p>
`

        funcMap := template.FuncMap{
                "upper": strings.ToUpper,
        }

        tmpl, err := template.New("example").Funcs(funcMap).Parse(templateText)
        if err != nil {
                panic(err)
        }

        data := struct {
                Name string
        }{
                Name: "John",
        }

        if err := tmpl.Execute(os.Stdout, data); err != nil {
                panic(err)
        }
}

講解:示例中使用了 len 內(nèi)置函數(shù)和自定義的 upper 函數(shù)。內(nèi)置函數(shù)可以直接在模板中使用,而自定義函數(shù)需要通過(guò) template.FuncMap 注冊(cè)。

自定義函數(shù)

自定義函數(shù)允許開(kāi)發(fā)者擴(kuò)展模板的功能。

package main

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

func main() {
    funcMap := template.FuncMap{
       "repeat": func(s string, count int) string {
          return strings.Repeat(s, count)
       },
    }

    const templateText = `{{repeat .Name 3}}`
    tmpl, err := template.New("example").Funcs(funcMap).Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Name string
    }{
       Name: "Go",
    }

    if err := tmpl.Execute(os.Stdout, data); err != nil {
       panic(err)
    }
}

講解:自定義函數(shù) repeat 使用 strings.Repeat 函數(shù)重復(fù)字符串,并通過(guò) template.FuncMap 注冊(cè)后在模板中使用。

管道

管道 (|) 允許將數(shù)據(jù)傳遞給多個(gè)函數(shù)進(jìn)行處理。

package main

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

func main() {
    funcMap := template.FuncMap{
       "trim":  strings.TrimSpace,
       "upper": strings.ToUpper,
    }

    const templateText = `{{.Name | trim | upper}}`
    tmpl, err := template.New("example").Funcs(funcMap).Parse(templateText)
    if err != nil {
       panic(err)
    }

    data := struct {
       Name string
    }{
       Name: "  go  ",
    }

    if err := tmpl.Execute(os.Stdout, data); err != nil {
       panic(err)
    }
}

講解:通過(guò)管道操作符 |,數(shù)據(jù) {{.Name}} 依次傳遞給 trim 和 upper 函數(shù)進(jìn)行處理,實(shí)現(xiàn)了多步驟的數(shù)據(jù)處理。

四、綜合使用

通過(guò)上述基礎(chǔ)功能、中級(jí)功能和高級(jí)功能的介紹,我們可以構(gòu)建一個(gè)功能完整的示例應(yīng)用。該示例將展示如何使用 text/template 構(gòu)建一個(gè)動(dòng)態(tài)生成 HTML 頁(yè)面的簡(jiǎn)單 Web 應(yīng)用。

package main

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

// 定義數(shù)據(jù)結(jié)構(gòu)
type PageData struct {
        Title   string
        Header  string
        Content string
        Items   []string
}

// 自定義函數(shù)
func trim(str string) string {
        return strings.TrimSpace(str)
}

func upper(str string) string {
        return strings.ToUpper(str)
}

func main() {
        // 創(chuàng)建模板函數(shù)映射
        funcMap := template.FuncMap{
                "trim":  trim,
                "upper": upper,
        }

        // 定義模板內(nèi)容
        const baseTemplate = `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{block "title" .}}Default Title{{end}}</title>
</head>
<body>
    {{template "header" .}}
    {{block "content" .}}{{end}}
    {{template "footer" .}}
</body>
</html>
`

        const headerTemplate = `
{{define "header"}}
<header>
    <h1>{{.Header | upper}}</h1>
</header>
{{end}}
`

        const footerTemplate = `
{{define "footer"}}
<footer>
    <p>Default Footer Content</p>
</footer>
{{end}}
`

        const contentTemplate = `
{{define "content"}}
<main>
    <p>{{.Content}}</p>
    <ul>
    {{range .Items}}
        {{template "item" .}}
    {{else}}
        <li>No items found</li>
    {{end}}
    </ul>
</main>
{{end}}
`

        const itemTemplate = `
{{define "item"}}
<li>{{. | trim | upper}}</li>
{{end}}
`

        // 解析所有模板
        tmpl := template.Must(template.New("base").Funcs(funcMap).Parse(baseTemplate))
        tmpl = template.Must(tmpl.Parse(headerTemplate))
        tmpl = template.Must(tmpl.Parse(footerTemplate))
        tmpl = template.Must(tmpl.Parse(contentTemplate))
        tmpl = template.Must(tmpl.Parse(itemTemplate))

        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                data := PageData{
                        Title:   "Go Template Best Practices",
                        Header:  "Welcome to Go Templates",
                        Content: "This is an example demonstrating various features of Go templates.",
                        Items:   []string{"Item 1", "Item 2", "Item 3"},
                }
                if err := tmpl.ExecuteTemplate(w, "base", data); err != nil {
                        http.Error(w, err.Error(), http.StatusInternalServerError)
                }
        })

        log.Println("Server started at :8080")
        http.ListenAndServe(":8080", nil)
}

主程序 (main.go)

  • 定義數(shù)據(jù)結(jié)構(gòu):PageData 結(jié)構(gòu)體用于傳遞數(shù)據(jù)給模板。
  • 自定義函數(shù):定義了 trim 和 upper 兩個(gè)自定義函數(shù),用于字符串處理。
  • 創(chuàng)建模板函數(shù)映射:使用 template.FuncMap 創(chuàng)建函數(shù)映射,以便在模板中使用自定義函數(shù)。
  • 定義模板內(nèi)容:將模板內(nèi)容作為字符串嵌入到 Go 代碼中,包括基礎(chǔ)模板和各個(gè)子模板。
  • 解析所有模板:使用 template.Must 和 template.New 解析所有模板字符串,并將函數(shù)映射添加到模板。
  • 處理 HTTP 請(qǐng)求:在 HTTP 處理函數(shù)中,創(chuàng)建 PageData 實(shí)例并傳遞給模板,通過(guò) ExecuteTemplate 方法渲染模板并輸出到瀏覽器。

基礎(chǔ)模板 (baseTemplate)

  • block 動(dòng)作:使用 block 動(dòng)作定義可重寫(xiě)的塊,如 title 和 content。子模板可以重寫(xiě)這些塊以實(shí)現(xiàn)模板繼承。
  • template 動(dòng)作:使用 template 動(dòng)作包含其他模板(如 header 和 footer),實(shí)現(xiàn)模板的模塊化和復(fù)用。

頭部模板 (headerTemplate)

  • 定義模板:使用 define 動(dòng)作定義一個(gè)可重用的模板塊 header。
  • 管道操作符:使用管道操作符將 Header 字段的值傳遞給 upper 函數(shù),轉(zhuǎn)換為大寫(xiě)。

頁(yè)腳模板 (footerTemplate)

  • 定義模板:定義一個(gè)簡(jiǎn)單的頁(yè)腳模板,包含固定的內(nèi)容。

內(nèi)容模板 (contentTemplate)

  • range 動(dòng)作:使用 range 動(dòng)作遍歷 Items 列表,為每個(gè)項(xiàng)目渲染 item 模板。如果列表為空,則顯示 else 分支中的內(nèi)容。
  • 包含模板:使用 template 動(dòng)作包含 item 模板,實(shí)現(xiàn)列表項(xiàng)的模塊化渲染。

列表項(xiàng)模板 (itemTemplate)

  • 定義模板:定義一個(gè)用于渲染單個(gè)列表項(xiàng)的模板。
  • 管道操作符:使用管道操作符將列表項(xiàng)值依次傳遞給 trim 和 upper 函數(shù),去除空格并轉(zhuǎn)換為大寫(xiě)。

運(yùn)行示例

  1. 將上述代碼保存到 main.go 文件中。
  2. 運(yùn)行 main.go 程序。
  3. 打開(kāi)瀏覽器并訪問(wèn) http://localhost:8080,查看渲染結(jié)果。

五、實(shí)際應(yīng)用

最近在寫(xiě)一個(gè) goctl web 的應(yīng)用來(lái)構(gòu)造 api 文件,通過(guò)前端 form 表單數(shù)據(jù)來(lái)對(duì) API 模板進(jìn)行數(shù)據(jù)渲染,模板內(nèi)容如下:

// generated by goctl.
syntax = "v1"

{{.types}}

{{range $group := .groups}}{{/* range route groups */}}
{{/* generate @server block */}}
{{with $group.server}}@server(
    {{if .jwt}}jwt: JWTAuth{{end}}
    {{if .prefix}}prefix: {{.prefix}}{{end}}
    {{if .group}}group: {{.group}}{{end}}
    {{if .timeout}}timeout: {{.timeout}}{{end}}
    {{if .middleware}}middleware: {{.middleware}}{{end}}
    {{if .maxBytes}}maxBytes: {{.maxBytes}}{{end}}
){{end}}
{{/* generate service block */}}
{{with $group.service}}service {{.name}}{
{{ $routes := .routes}} {{/* define a variable to block the follows range block */}}
{{range  $idx, $route := .routes}}{{/* releace $route to dot */}}
    @handler {{$route.handlerName}}
    {{$route.method}} {{$route.path}} {{if $route.request}}({{$route.request}}){{end}} {{if $route.response}}returns ({{$route.response}}){{end}}{{if lessThan $idx (len $routes)}}
{{end}}
{{end}}}{{end}}
{{end}}

模板頭部

// generated by goctl.
syntax = "v1"

{{.types}}

// generated by goctl.:注釋?zhuān)硎具@個(gè)文件是由 goctl 工具生成的。

syntax = "v1":定義了語(yǔ)法版本為 v1。

{{.types}}:插入模板上下文中的結(jié)構(gòu)體數(shù)據(jù)。

遍歷分組(range)

{{range $group := .groups}}{{/* range route groups */}}

{{range $group := .groups}}:遍歷模板上下文中的 groups 路由分組列表,每個(gè)路由分組 group 被賦值給變量 $group。

生成 @server 塊

{{with $group.server}}@server(
    {{if .jwt}}jwt: JWTAuth{{end}}
    {{if .prefix}}prefix: {{.prefix}}{{end}}
    {{if .group}}group: {{.group}}{{end}}
    {{if .timeout}}timeout: {{.timeout}}{{end}}
    {{if .middleware}}middleware: {{.middleware}}{{end}}
    {{if .maxBytes}}maxBytes: {{.maxBytes}}{{end}}
){{end}}

{{with $group.server}}:進(jìn)入 $group.server 子模板上下文,將$group.server重新賦值到{{.}},減少冗長(zhǎng)的鏈?zhǔn)秸{(diào)用。

{{if .jwt}}jwt: JWTAuth{{end}}:如果 jwt 存在,則生成 jwt: JWTAuth,這里用到了條件動(dòng)作,其他幾個(gè) if 條件類(lèi)似。

{{end}}:結(jié)束 with 動(dòng)作。

生成 service 塊

{{/* generate service block */}}
{{with $group.service}}service {{.name}}{
{{ $routes := .routes}} {{/* define a variable to block the follows range block */}}

{{/\* generate service block \*/}}用到了注釋動(dòng)作,記得注釋的/* 后和 */要有空格。

{{with $group.service}}:進(jìn)入 $group.service 子模板上下文,將 $group.service 賦值到{{.}},減少冗長(zhǎng)的鏈?zhǔn)秸{(diào)用。

service {{.name}}{:生成 service <name>{,其中 <name> 是 service 的名稱(chēng)。

{{ $routes := .routes}}:定義一個(gè)臨時(shí)變量 $routes 保存 routes 列表,用于突破下文的 range 上下文。

遍歷 routes 列表并生成每個(gè)路由

{{range  $idx, $route := .routes}}{{/* releace $route to dot */}}
    @handler {{$route.handlerName}}
    {{$route.method}} {{$route.path}} {{if $route.request}}({{$route.request}}){{end}} {{if $route.response}}returns ({{$route.response}}){{end}}{{if lessThan $idx (len $routes)}}
{{end}}
{{end}}}{{end}}

{{range $idx, $route := .routes}}:遍歷 routes 列表,每個(gè) route 被賦值給 $route,索引被賦值給 $idx。

@handler {{$route.handlerName}}:生成 @handler <handlerName>,其中 <handlerName> 是處理器名稱(chēng)。

{{$route.method}} {{$route.path}}:生成 <method> <path>,其中 <method> 是 HTTP 方法,<path> 是路徑。

{{if $route.request}}({{$route.request}}){{end}}:如果 request 存在,則生成 (<request>)。

{{if $route.response}}returns ({{$route.response}}){{end}}:如果 response 存在,則生成 returns (<response>)。

{{if lessThan $idx (len $routes)}}:檢查當(dāng)前索引是否小于 routes 列表的長(zhǎng)度。lessThan 用到自定義函數(shù)功能。

如下是模板數(shù)據(jù)填充的部分代碼:

var data []KV
for _, group := range mergedReq.List {
    var groupData = KV{}
    var hasServer bool
    var server = KV{}
    if group.Jwt {
       hasServer = true
       server["jwt"] = group.Jwt
    }
    if len(group.Prefix) > 0 {
       hasServer = true
       server["prefix"] = group.Prefix
    }
    if len(group.Group) > 0 {
       hasServer = true
       server["group"] = group.Group
    }
    if group.Timeout > 0 {
       hasServer = true
       server["timeout"] = fmt.Sprintf("%dms", group.Timeout)
    }
    if len(group.Middleware) > 0 {
       hasServer = true
       server["middleware"] = group.Middleware
    }
    if group.MaxBytes > 0 {
       hasServer = true
       server["maxBytes"] = group.MaxBytes
    }

    if hasServer {
       groupData["server"] = server
    }

    var routesData []KV
    for _, route := range group.Routes {
       var request, response string
       if len(route.RequestBody) > 0 {
          request = l.generateTypeName(route, true)
       }
       if !util.IsEmptyStringOrWhiteSpace(route.ResponseBody) {
          response = l.generateTypeName(route, false)
       }
       routesData = append(routesData, KV{
          "handlerName": l.generateHandlerName(route),
          "method":      strings.ToLower(route.Method),
          "path":        route.Path,
          "request":     request,
          "response":    response,
       })
    }
    var service = KV{
       "name":   req.Name,
       "routes": routesData,
    }
    groupData["service"] = service
    data = append(data, groupData)
}

t, err := template.New("api").Funcs(map[string]any{
    "lessThan": func(idx int, length int) bool {
       return idx < length-1
    },
}).Parse(apiTemplate)
if err != nil {
    return nil, err
}

tps, err := l.generateTypes(mergedReq.List)
if err != nil {
    return nil, err
}

var typeString string
if len(tps) > 0 {
    typeString = strings.Join(tps, "\n\n")
}

w := bytes.NewBuffer(nil)
err = t.Execute(w, map[string]any{
    "types":  typeString,
    "groups": data,
})
if err != nil {
    return nil, err
}

formatWriter := bytes.NewBuffer(nil)
err = format.Source(w.Bytes(), formatWriter)
if err != nil {
    return nil, err
}

最后在 web 頁(yè)面上展示如圖,圖中右邊的 api 內(nèi)容就是由模板渲染出來(lái)的。

https://gen.go-zero.dev/

圖片圖片

六、總結(jié)

本文介紹了 Go 語(yǔ)言中 text/template 包的基礎(chǔ)功能、中級(jí)功能和高級(jí)功能,并通過(guò)具體示例講解了每個(gè)功能的使用方法。通過(guò)這些示例,我們可以看到 text/template 包的強(qiáng)大功能以及在實(shí)際開(kāi)發(fā)中的廣泛應(yīng)用。希望本文能幫助您更好地理解和使用 text/template,構(gòu)建出更加靈活和高效的數(shù)據(jù)驅(qū)動(dòng)應(yīng)用。

項(xiàng)目地址

https://github.com/zeromicro/go-zero

責(zé)任編輯:武曉燕 來(lái)源: 微服務(wù)實(shí)踐
相關(guān)推薦

2012-03-29 13:17:27

GoogleWEB

2010-06-12 21:59:39

融合網(wǎng)絡(luò)數(shù)據(jù)中心博科

2016-06-16 09:56:17

Cloudera

2010-04-30 09:19:05

Servlet 3.0

2017-06-14 09:00:40

容器開(kāi)發(fā)人員云應(yīng)用

2009-09-22 13:04:19

ibmdwREST

2019-07-24 10:34:28

Spring Boot項(xiàng)目模板

2013-05-17 09:41:02

Node.js云應(yīng)用開(kāi)發(fā)IaaS

2014-12-30 11:37:38

bmobapp

2015-03-13 15:58:11

Adobe

2020-01-03 14:03:46

云計(jì)算開(kāi)發(fā)云原生

2013-11-12 09:47:17

OpenStack HOpenStack開(kāi)源

2024-04-28 10:22:08

.NETMVVM應(yīng)用工具包

2022-07-11 08:16:55

策略模式if-else

2013-07-04 13:19:24

Java開(kāi)發(fā)速度

2011-01-24 13:20:49

2011-12-05 14:50:13

Knockout

2015-02-11 09:15:46

云部署嵌套虛擬化PaaS

2010-01-05 17:07:03

Ubuntu Shut
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)