当前位置: 首页 > news >正文

《Go Web 编程》之第5章 内容展示

《Go Web 编程》之第5章 内容展示

  • 第5章 内容展示
    • 5.1 模板引擎
    • 5.2 Go的模板引擎
      • 5.2.1 模板进行语法分析
      • 5.2.2 执行模板
    • 5.3 动作
      • 5.3.1 条件动作
      • 5.3.2 迭代动作
      • 5.3.3 设置动作
      • 5.3.4 包含动作
    • 5.4 参数、变量和管道
    • 5.5 函数
    • 5.6 上下文感知
      • 5.6.1 防御XSS攻击
      • 5.6.2 不对HTML进行转义
    • 5.7 嵌套模板
    • 5.8 通过块动作定义默认模板

第5章 内容展示

5.1 模板引擎

模板引擎通过将数据和模板组合在一起生成HTML。

  • 无逻辑模板引擎(logic-less template engine),只进行占位符的字符串替换,分离表现和逻辑,计算交给处理器完成。
  • 嵌入逻辑的模板引擎(embedded logic template engine),编程语言嵌入模板中,功能强大,逻辑分布于不同处理器间,代码难维护。

5.2 Go的模板引擎

模板引擎通过模板文件列表和传入的动态数据,生成HTML,写入ResponseWriter。

模板引擎两个步骤:
(1)模板文件列表或者字符串进行语法分析,创建模板结构;
(2)模板引擎结合模板结构和传入动态数据,开始执行,生成HTML,传递给ResponseWriter。

tmpl.html

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ . }}
  </body>
</html>
package main

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

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("tmpl.html")
	t.Execute(w, "Hello World!")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

5.2.1 模板进行语法分析

//可接收多个文件,返回一个模板集合(名称与第一个文件名相同)
t, _ := template.ParseFiles("tmpl.html")
//等价于
t := template.New("tmpl.html")
t, _ := t.ParseFiles("tmpl.html")

//匹配给定模式的所有文件进行语法分析
t, _ := template.ParseGlob("*.html")

//非nil错误,产生panic
t := template.Must(template.ParseFiles("tmpl.html"))
t, _ := template.ParseFiles("tmpl.html")
//等价于
t := template.New("tmpl.html")
tmpl := `<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ . }}
  </body>
</html>
`
t, _ := t.Parse(tmpl)

5.2.2 执行模板

t, _ := template.ParseFiles("t1.html", "t2.html")

//默认执行t1.html模板
t.Execute(w, "Hello World!")

//执行t2.html模板
t.ExecuteTemplate(w, "t2.html", "Hello World!")

5.3 动作

{{和}}包围的命令。

  • 条件动作
  • 迭代动作
  • 设置动作
  • 包含动作
  • 定义动作

.也是动作,代表传递的数据。

5.3.1 条件动作

{{ if arg }}
	some content
{{ end }}
{{ if arg }}
	some content
{{ else }}
	other content
{{ end }}

tmpl.html

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ if . }}
      Number is greater than 5!
    {{ else }}
      Number is 5 or less!
    {{ end }}
  </body>
</html>
package main

import (
	"html/template"
	"math/rand"
	"net/http"
	"time"
)

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("tmpl.html")
	rand.Seed(time.Now().Unix())
	t.Execute(w, rand.Intn(10) > 5)
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

5.3.2 迭代动作

数组、切片、映射或者通道进行迭代,内部{{ . }}代表迭代元素。

{{ range array }}
	Dot is set to the element {{ . }}
{{ end }}

tmpl.html

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <ul>
    {{ range . }}
      <li>{{ . }}</li>
  	{{ else }}
      <li>Nothing to show</li>
    {{ end }}
	
    </ul>
  </body>
</html>
package main

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

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("tmpl.html")
	daysOfWeek := []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
	t.Execute(w, daysOfWeek)
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

5.3.3 设置动作

指定范围内为点(.)设置值。

{{ with arg }}
	Dot is set to arg
{{ end }}

tmpl.html

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <div>The dot is {{ . }}</div>
    <div>
    {{ with "world"}}
      	Now the dot is set to {{ . }}
    {{ end }}
    
    {{ with ""}}
      	Now the dot is set to {{ . }}
     {{ else }}
     	The dot is still {{ . }}
    {{ end }}
    </div>
    <div>The dot is {{ . }} again</div>
  </body>
</html>
package main

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

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("tmpl.html")
	t.Execute(w, "hello")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

5.3.4 包含动作

嵌套模板。

{{ template "name" }}

t1.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=9">
    <title>Go Web Programming</title>
  </head>
  <body>    
    <div> This is t1.html before</div>
    <div>This is the value of the dot in t1.html - [{{ . }}]</div>
    <hr/>
    {{ template "t2.html" . }}
    <hr/>
    <div> This is t1.html after</div>
  </body>
</html>

t2.html

<div style="background-color: yellow;">
  This is t2.html<br/>
  This is the value of the dot in t2.html - [{{ . }}]
</div>
package main

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

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("t1.html", "t2.html")
	t.Execute(w, "Hello World!")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

5.4 参数、变量和管道

参数(argument)是模板中值。
布尔、整数、字符串;
结构、结构字段、数组;
变量、方法(只返回一个值或者值+error)、函数;
处理器向模板引擎传递的数据,点(.)。

arg是参数。

{{ if arg }}
	some content
{{ end }}

动作中设置变量,以$开头。

$variable := value

变量实现迭代。

{{ range $key, $value := . }}
	The key is {{ $key }} and the value is {{ $value }}
{{ end }}

模板中管道(pipeline)是多个有序串联的参数、函数和方法。

{{ p1 | p2 | p3 }}
<!DOCTYPE html>
<html>
  <head>
    <title>Go Web Programming</title>
  </head>
  <body>
  	{{ 12.3456 | printf "%.2f" }}
  </body>
</html>

5.5 函数

模板引擎函数可输入多个参数,只返回一个值或者值+error
自定义模板函数:
(1)创建FuncMap映射,键为函数名(自定义字符串),值为实际定义的函数;
(2)FuncMap与模板绑定。
tmpl.html

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <div>The date/time is {{ . | fdate }}</div>
    <div>The date/time is {{ fdate . }}</div>
  </body>
</html>
package main

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

func formatDate(t time.Time) string {
	layout := "2006-01-02"
	return t.Format(layout)
}

func process(w http.ResponseWriter, r *http.Request) {
	funcMap := template.FuncMap{"fdate": formatDate}
	t := template.New("tmpl.html").Funcs(funcMap)
	t, _ = t.ParseFiles("tmpl.html")
	t.Execute(w, time.Now())
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

5.6 上下文感知

模板以上下文感知(context-aware)的方式显示内容,根据内容所处的上下文改变其显示的内容。

tmpl.html

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <div>{{ . }}</div>
    <div><a href="/{{ . }}">Path</a></div>
    <div><a href="/?q={{ . }}">Query</a></div>
    <div><a onclick="f('{{ . }}')">Onclick</a></div>
  </body>
</html>
package main

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

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("tmpl.html")
	content := `I asked: <i>"What's up?"</i>`
	t.Execute(w, content)
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}
curl -i 127.0.0.1:8080/process
HTTP/1.1 200 OK
Date: Mon, 12 Sep 2022 13:52:10 GMT
Content-Length: 519
Content-Type: text/html; charset=utf-8

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <div>I asked: &lt;i&gt;&#34;What&#39;s up?&#34;&lt;/i&gt;</div>
    <div><a href="/I%20asked:%20%3ci%3e%22What%27s%20up?%22%3c/i%3e">Path</a></div>
    <div><a href="/?q=I%20asked%3a%20%3ci%3e%22What%27s%20up%3f%22%3c%2fi%3e">Query</a></div>
    <div><a onclick="f('I asked: \u003ci\u003e\u0022What\u0027s up?\u0022\u003c\/i\u003e')">Onclick</a></div>
  </body>
</html>

5.6.1 防御XSS攻击

持久性XSS漏洞(persistent XSS vulnerability),常见XSS攻击方式,由于服务器将攻击者存储的数据原原本本地显示给其它用户所致。比如攻击者数据含<script>标签,预防方法是对用户数据进行转义存储或显示。
form.html

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <form action="/process" method="post">
      Comment: <input name="comment" type="text" size="50">
     <hr/>
     <button id="submit">Submit</button>
    </form>
  </body>
</html>

tmpl.html

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <div>{{ . }}</div>
  </body>
</html>
package main

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

func process(w http.ResponseWriter, r *http.Request) {  
  t, _ := template.ParseFiles("tmpl.html")  
  t.Execute(w, r.FormValue("comment"))
}

func form(w http.ResponseWriter, r *http.Request) {  
  t, _ := template.ParseFiles("form.html")  
  t.Execute(w, nil)  
}

func main() {
  server := http.Server{
    Addr: "127.0.0.1:8080",
  }
  http.HandleFunc("/process", process)
  http.HandleFunc("/", form)
  server.ListenAndServe()
}
http://127.0.0.1:8080/form
文本框输入<script>alert('Pwnd!');<script>,点击Submint按钮

网页源码:
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    <div>&lt;script&gt;alert(&#39;Pwnd!&#39;);&lt;script&gt;</div>
  </body>
</html>

5.6.2 不对HTML进行转义

package main

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

func process(w http.ResponseWriter, r *http.Request) {  
  //关闭浏览器内置的XSS防御功能
  w.Header().Set("X-XSS-Protection", "0")
  t, _ := template.ParseFiles("tmpl.html")
  //不转换HTML
  t.Execute(w, template.HTML(r.FormValue("comment")))
}

func form(w http.ResponseWriter, r *http.Request) {  
  t, _ := template.ParseFiles("form.html")  
  t.Execute(w, nil)  
}

func main() {
  server := http.Server{
    Addr: "127.0.0.1:8080",
  }
  http.HandleFunc("/process", process)
  http.HandleFunc("/", form)
  server.ListenAndServe()
}

5.7 嵌套模板

布局(layout),Web设计中可在多个页面重复使用的固定模式。头部菜单,提供服务器状态、版权声明、联系方式等附件信息的尾部栏,导航栏,多级菜单等布局可通过嵌套模板实现。

  1. 一个模板文件里定义多个模板

tmpl.html

{{ define "layout" }}
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ template "content" }}
  </body>
</html>
{{ end }}

{{ define "content" }}
Hello World!
{{ end }}
package main

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

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("layout.html")
	//t.ExecuteTemplate(w, "layout", "")
	t.ExecuteTemplate(w, "layout", nil)
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}
curl -i http://127.0.0.1:8080/process
  1. 不同模板文件定义同名模板

red_hello.html

{{ define "content" }}
<h1 style="color: red;">Hello World!</h1>
{{ end }}

blue_hello.html

{{ define "content" }}
<h1 style="color: blue;">Hello World!</h1>
{{ end }}

layout.html

{{ define "layout" }}
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ template "content" }}
  </body>
</html>
{{ end }}
package main

import (
	"html/template"
	"math/rand"
	"net/http"
	"time"
)

func process(w http.ResponseWriter, r *http.Request) {
	rand.Seed(time.Now().Unix())
	var t *template.Template
	if rand.Intn(10) > 5 {
		t, _ = template.ParseFiles("layout.html", "red_hello.html")
	} else {
		t, _ = template.ParseFiles("layout.html", "blue_hello.html")
	}
	t.ExecuteTemplate(w, "layout", "")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}
  1. 定义默认模板布局

layout.html

{{ define "layout" }}
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ template "content" }}
  </body>
</html>
{{ end }}

{{ define "content" }}
<h1 style="color: blue;">Hello World!</h1>
{{ end }}

red_hello.html

{{ define "content" }}
<h1 style="color: red;">Hello World!</h1>
{{ end }}
package main

import (
	"html/template"
	"math/rand"
	"net/http"
	"time"
)

func process(w http.ResponseWriter, r *http.Request) {
	rand.Seed(time.Now().Unix())
	var t *template.Template
	if rand.Intn(10) > 5 {
		t, _ = template.ParseFiles("layout.html", "red_hello.html")
	} else {
		t, _ = template.ParseFiles("layout.html")
	}
	t.ExecuteTemplate(w, "layout", "")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

5.8 通过块动作定义默认模板

块动作(block action),定义模板并且立即使用。

{{ block arg }}
	Dot is set to arg
{{ end }}

red_hello.html

{{ define "content" }}
<h1 style="color: red;">Hello World!</h1>
{{ end }}

layout.html

{{ define "layout" }}
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ black "content" . }}
	<h1 style="blue: red;">Hello World!</h1>
    {{ end }}
  </body>
</html>
{{ end }}
package main

import (
	"html/template"
	"math/rand"
	"net/http"
	"time"
)

func process(w http.ResponseWriter, r *http.Request) {
	rand.Seed(time.Now().Unix())
	var t *template.Template
	if rand.Intn(10) > 5 {
		t, _ = template.ParseFiles("layout.html", "red_hello.html")
	} else {
		t, _ = template.ParseFiles("layout.html")
	}
	t.ExecuteTemplate(w, "layout", "")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

相关文章:

  • linux环境搭建nacos集群详解
  • 【Linux】进程概念(下篇) —— 程序地址空间详解
  • Windows上部署Discuz论坛
  • 猿创征文|我的C/C++技术成长之路
  • 微信小程序在线考试项目开发-用户信息注册登录功能
  • 微服务项目:尚融宝(36)(核心业务流程:用户绑定(1))
  • 2023秋招的第一个意向书
  • 正点原子Linux MINI板系统固化(烧录uboot、linux kernel、.dtb(设备树)和 rootfs)
  • 核酸检测小程序实战教程
  • MATLAB | 绘制博士学位的图解指南
  • 单词相似性查询易语言代码
  • 【Vue】Vue项目需求--实现搜索框输入防抖处理
  • 03Python数据类型
  • CSS常见选择器
  • React-函数组件的特性与闭包
  • SegmentFault for Android 3.0 发布
  • [rust! #004] [译] Rust 的内置 Traits, 使用场景, 方式, 和原因
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • Apache的80端口被占用以及访问时报错403
  • docker-consul
  • Effective Java 笔记(一)
  • jquery cookie
  • Python连接Oracle
  • select2 取值 遍历 设置默认值
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 你真的知道 == 和 equals 的区别吗?
  • 深度学习入门:10门免费线上课程推荐
  • 微服务框架lagom
  • 系统认识JavaScript正则表达式
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 责任链模式的两种实现
  • 正则与JS中的正则
  • Spring Batch JSON 支持
  • ​卜东波研究员:高观点下的少儿计算思维
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • $NOIp2018$劝退记
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (四) 虚拟摄像头vivi体验
  • *1 计算机基础和操作系统基础及几大协议
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .NET NPOI导出Excel详解
  • .net 程序发生了一个不可捕获的异常
  • .NET 反射的使用
  • .Net 应用中使用dot trace进行性能诊断
  • /usr/bin/env: node: No such file or directory
  • @private @protected @public
  • @test注解_Spring 自定义注解你了解过吗?
  • @Transactional 竟也能解决分布式事务?
  • [ SNOI 2013 ] Quare
  • [14]内置对象
  • [Android Studio] 开发Java 程序