Go web
工作流程
- 客户端向服务器发送请求
- 多路复用器接收到请求,并将其重定向到正确的处理器
- 处理器对请求进行处理
- 在需要访问数据的情况下,处理器会使用一个或多个数据结构,这些数据结构都是根据数据库中的数据建模而来的
- 当处理器调用与数据结构有关的函数或者方法时,这些数据结构背后的模型会与数据库进行连接,并执行相应的操作。
- 当请求处理完毕时,处理器会调用模板引擎,有时候还会向模板引擎传递一些通过面模型获取到的数据
- 模板引擎会对模板文件进行语法分析并创建对应的模板,而这些模板又会与处理器传递的数据一起合并生成最终的HTML
- 生成的HTML会作为相应的一部分回传至客户端
接收请求
net/http标椎库
- Client,Response,Header,Request和Cookie对客户端进行支持
- Server,ServerMux,Handler/HandleFunc,ResponseWriter,Header,Request和Cookie则对服务器进行支持。
Go web
创建一个服务器的步骤非常简单,只要调用ListenAndServer并传入网络地址以及负责处理请求的处理器(handler)作为参数就行了。如果网络地址参数为空字符串,那么服务器默认使用80端口进行网络连接;如果处理器参数为nil,那么服务器将使用默认的多路复用器DefaultServeMux.
func main(){
http.ListenAndServe("",nil)
}
用户可以通过ListenAndServe的参数对服务器的网络地址和处理器进行配置之外,还可以通过Server结构对服务器进行更为详细的配置,其中包括请求读取操作设置超时时间,为响应写入操作设置超时时间以及为Server结构设置错误日志记录器等。
func main(){
server := http.Server{
Addr : "127.0.0.1:8080",
Handler : nil,
}
server.ListenAndServe()
}
server的结构配置选项
type Server struct{
Addr string
Handler Handler
ReadTimeout time.Duration
WriteTimeout time.Duration
MaxHeaderBytes int
TLSConfig *tls.Config
TLSNextProto map[string]func(*Server,*tls.Conn,Handler)
ConnState func(net.Conn,ConnState)
ErrorLog *log.Logger
}
HTTPS
当客户端和服务器需要共享密码或者信用卡信息这样私密信息是,大多数网站都会使用HTTPS对客户端和服务器之间的通信进行加密和保护。
HTTPS实际上就是将HTTP通信放到SSL之上进行。通过使用ListenAndServerTLS函数,可以让简单的web提供HTTPS服务。
func main(){
server : http.Server{
Addr : "127.0.0.1:8080",
Handler : nil,
}
server.ListenAndServerTLS("cert.pem","key.pem")
}
Cert.pem文件是SSL证书,而key.pem则是服务器的私钥。在生产环境中使用的SSL证书需要通过CA取得,但如果是出于测试目的使用的证书和私钥,使用自行生成就可以了。Go标准库中crypto包群。
SSL,TLS,HTTPS
SSL(Sevure Socket Layer,安全套接字)是一种通过公钥基础设施为通信双方提供数据加密和身份验证的协议,其中通信的双发通常是客户端和服务端。之后被IETF(互联网工程任务组)接收并将其改名为TLS(Transport Layer Security).HTTPS,即SSL之上的HTTP,实际上就是在SSL/TLS连接的上层进行HTTP通信。
HTTPS需要使用SSL/TLS证书来实现数据加密以及身份验证。SSL证书存储在服务器上,它是一种使用X.509格式进行格式化的数据,这些数据包含了公钥以及其他一些相关信息。为了保证证书的可靠性,证书一般由证书分发机构(CA)签发。服务器在接收到客户端发送的请求之后,会将证书和响应一并返回客户端,而客户端在确认证书的真实性之后,会生成一个随机秘钥,并使用证书中的公钥对随机密钥进行加密,此较慢产生的堆成密钥就是客户端和服务器在进行通信时,负责对通信实施加密的实际密钥。
处理器和处理器函数
处理器
在Go语言中,一个处理器就是一个拥有ServeHTTP方法的接口,这个ServeHTTP方法需要接受两个参数:第一个参数是一个ResponseWriter接口,而第二个参数则是一个指向Request结构的指针。
任何接口只要拥有一个ServeHTTP方法,并且该方法带有以下签名,那么这个方法就是一个处理器。
ServeHTTP(http.ResponseWriter,*heep.Request)
ListenAndServe的第二个参数是一个处理器,但默认值是多路复用器DefaultServeMux.
DefaultServeMux多路复用器是ServeMux结构的一个实力,而后者也拥有ServeHTTP方法,并且这个方法的签名与称为处理器所需的签名完全一致。
DefaultServeMux即使一个多路复用器,还是一个处理器。唯一要做的就是根据请求的URL将请求重定向到不同的处理器。
type MyHandler struct{}
func (h *MyHandler) ServerHTTP(w http.ResponseWrite ,r *http.Request){
fmt.Fprintf(w,"hello world")
}
func main(){
handler := MyHandler()
server := http.Server{
Addr := "127.0.0.1:8080",
Handler: &handler, // 用一个处理器处理所有的请求
}
server.ListenAndServe()
}
多个处理器
大部分情况下,并不会使用一个处理器去处理所有的请求,而是希望使用多个处理器去处理不同的URL。为了做到这一点不在使用Server结构的Handler字段中指定处理器,而是让服务器使用默认的DefaultServeMux作为处理器。然后通过http.Handle函数将处理器绑定至DafultServeMux.
Handle函数源于http包,但实际上是ServeMux结构的方法: 这些函数是为了操作便利而创建的函数,调用它们等同调用DefaultServeMux的某个方法。
type HelloHandler struct{}
func (h *HelloHandler) ServerHTTP(w http.ResponseWrite ,r *http.Request){
fmt.Fprintf(w,"hello")
}
type WorldHandler struct{}
func (h *WorldHandler) ServerHTTP(w http.ResponseWrite ,r *http.Request){
fmt.Fprintf(w,"world")
}
func main(){
hello := HelloHandler()
world := WorldHandler()
server := http.Server{
Addr : "127.0.0.1:8080", //默认使用DafaultServeMux 多路复用器
}
http.Handle("/hello",&hello) // 将自定义的处理器绑定到多路复用器上
http.Handle("/world",&world)
server.ListenAndServe()
}
处理器函数
处理器函数实际上就是与处理器拥有相同行为的函数:这些函数与ServeHTTP方法拥有相同的签名。
func hello(w http.ResponseWriter,r *http.Request){
fmt.Fprintf(w,"hello")
}
func world(w http.ResponseWriter,r *http.Request){
fmt.Fprntf(w,"world")
}
func main (){
server := http.server{
Addr :="127.0.0.1:8080",
}
http.HandleFunc("/hello",hello)
http.HandleFunc("/wordl",world)
server.ListenAndServe()
}
处理器函数的实现原理是这样的:Go语言拥有一种HandlerFunc函数类型,可以将一个带有正确签名的函数f转换成一个带有方法f的Hadnler.
虽然处理器函数能够完成跟处理器一样的工作,并且使用处理器函数的代码比使用处理器的代码更为整洁,但处理函数并不能完全代替处理器。这是在某些情况下,代码可能已经包含了某个接口或者某种类型,这时候只需要添加ServeHTTP方法就可转化成处理器了,并且这种转变也有助于构建出更为模块化的web应用。
串联处理器和处理器函数
在Go语言,程序可以将一个函数传递给另一个函数,又或者通过标识符去引用一个具名函数。
这意味着程序可以将函数f1传递给另一个函数f2,然后函数f2执行完某些操作之后调用f1。
串联两个处理器函数
func hello(w http.ResponseWriter,r *http.Request){
fmt.Fprintf(w,"Hrllo!")
}
func log(h http.Handlerfunc) http.HandlerFunc {
return func(w http.ResponseWriter ,r *http.Request){
name := runtine.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
fmt.Println("Handler function called -"+name)
h(w,r)
}
}
func main(){
server := server{
Addr : "127.0.0.1:8080"
}
http.HandleFunc("/hello",log(hello))
server.ListenAndServe()
}
多路复用器
ServeMux是一个HTTP请求多路复用器,负责接收HTTP请求并根据请求中的URL将请求重定向到正确的处理器。
ServeMux结构包含了一个映射,这个映射会将URL映射至向相应的处理器。ServeMux结构也实现了ServeHTTP方法,所以它也是一个处理器。当ServeMux的ServeHTTP方法接收到一个请求时,会在结构的映射里面找出与被请求URL最为匹配的URL,然后调用与之对应的处理器的ServeHTTP方法。
DefaultServeMux并不是ServeMux的实现,DefaultServeMux是ServeMux的一个实例,并且所有引入了net/http标准库的程序都可以使用这个实例。当用户没有为Server结构指定处理器是,服务器就会使用DeafultServeMux作为ServeMux的默认实例。
DefaultServeMux请求路径匹配原则,如果绑定的URL不是以/结尾,那么只会与完全相同的URL匹配;但如果绑定的URL以/结尾,那么即使请求的URL只有前缀部分与被绑定的URL相同,ServeMux也会认定这两个URL是匹配的。
处理请求
请求构成
Request 结构
Request结构表示一个由客户端发送的HTTP请求报文。虽然HTTP请求报文是由一系列文本组成的 ,但Request结构并不是完全按照报文逐字逐句定义的。这个结构只包含了报文在经过语法分析之后,较为重要的信息。
Request结构的的组成
- URL字段
- Header字段
- Body字段
- From字段,PostFrom字段和MultipartFrom字段
通过Request结构的方法,用户还可以对请求报文中的cookie,引用URL以及用户代理进行访问。当net/http库被用作HTTP客户端的时候,Request结构即可用于表示客户端将要发送给服务端的请求,也可以用于表示服务器接收客户端请求。
请求URL
Request结构中的URL字段用于表示请求行中包含的URL,这个字段是一个指向url.URL的结构的指针。
type URL struct{
Scheme string
Opaque string
User string
Host string
Path string
RawQuery string
Fragment string
}
请求构成
请求和响应的首部都使用Header类型描述,这种类型使用一个映射来表示HTTP首部中的多个键值对。Header类型有4种基本方法,这种方法可以根据给定的键执行添加,删除,获取和设置值等操作。
一个Header类型的实例就是一个映射,这个映射的键为字符串,而键的值则是由任意多个字符串组成的切片。为Header类型设置首部以及添加首部都是非常简单的。在对Header执行设置操作时,给定键的值首先会被设置成一个空白的字符串切片,然后改切片中的第一个元素会被设置给定的首部值,而在对Header执行添加操作是,给定的首部值会被添加到字符串切片已有元素的后面。
请求主体
请求和响应的主体都由Request结构和Body字段表示,这个字段是一个io.ReadCloser接口,该接口即包含了Reader接口,也包含了Closer接口。其中Reader接口拥有Read方法,这个方法接受一个字节切片为输入,并在执行之后返回被读取内容的字节数以及一个可选的错误作为结果,而Closer接口则拥有Close方法,这个方法不接受任何参数,但会在出错是返回一个错误。同时包含Reader接口和Closer接口意味着用户可以对Body字段调用Read方法和Close方法。
表单
From字段
为了提取表单传递的键值对数据,用户可能需要亲自对服务器接收到的未经处理的表单数据进行语法分析。
通过调用Request结构提供的方法,用户可以将URL,主体又或者以上两者记录的数据提取到该结构的From,PostFrom和MultipartFrom等字段当中。
使用Request结构的方法获取表单数据的一般步骤是:
- 调用ParseForm方法或者ParseMultipartFrom方法,对请求进行语法分析。
- 根据步骤1调用的方法,访问响应的From字段,PostFrom字段或MultiparFrom字段
func process(w hhtp.ResponseWriter,r *http.Request){
r.ParseFrom()
fmt.Fprintln(w,r.From)
}
func main(){
server := http.Server{
Addr : "127.0.0.1:8080"
}
http.HandleFunc("/process",process)
server.ListenAndServe()
}
服务器在对请求进行语法分析之后,使用字符串形式显示出来的未经处理的From结构。这个结构是一个映射,他的键是字符串,而建的值是一个由字符串组成的切片。应为映射是无序的,所以键值对排列顺序显示的可能不同。
文件
Multipart/from-data 编码通常用于实现文件上传功能,这种功能需要用到file类型的input标签。
func zxt(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(1024)
fileHeader := r.MultipartForm.File["uploaded"][0] // 获取名字为uploaded字段的内容
file ,err := fileHeader.Open()
if err==nil {
data,err:=ioutil.ReadAll(file)
if err ==nil {
fmt.Fprintln(w,string(data))
}
}
}
ResponseWriter
首先创建一个Response结构,接着将数据存储到这个结构里面,,最后将这个结构返回给客户端。服务端在向客户端返回响应的时候,真正需要用的是ResponseWriter接口。
ResponseWriter接口拥有以下3个方法:
- Write;
- WriteHeader;
- Header;
Write 方法接受一个字节数组作为参数,并将数组中的字节写入HTTP响应的主体中。如果用户在使用Write方法执行写入操作时,没有为首字母设置相应的内容类型,那么响应的内容类型将通过检测被写入前512字节。
func writeExample(w http.ResponseWriter,r *http.Request){
str :=‘<html>
<head><title></title></head>
</html>’
w.Write([]byte(str))
}
WriteHeader方法不能用于设置响应的首部(Header方法设置响应首部):WriteHeader方法接受一个代表HTTP响应状态码的整数作为参数,并将这个整数作为HTTP相应的返回状态码。在调用这个方法之后,用户可以继续对ResponseWriter进行写入,但是不能对响应首部做任何写入操作。如果用户在调用Write方法之前没有执行WriteHeader方法,那么程序默认会使用200 ok作为相应的状态码。
func writeHeader(w http.ResponseWriter,r *http.Request){
w.WriteHeader(501)
fmt.Fprintln(w,"No service")
}
Header方法可以获取一个由首部组成的映射,修改这个映射就可以修改首部,修改这个映射就可以修改首部,修改后的首部将被包含在HTTP响应里面,并随着响应一同发送至客户端。
func header(w http.ResponseWriter,r *http.Request){
w.Header().Set("Location","http://www.zxt.com")
w.WriteHeader(302)
}
向客户端返回一个JSON数据的方法
func jsonExample(w http.ResponseWriter,r *http.Request){
w.Header().Set("Content-Type","application/json")
post := &Post{
User : "zxt"
Thread : []string{"first","second","third"}
}
json, _ := json.Marshal(post)
w.Write()
}
cookie
cookie是一种存储在客户端的,体积较小的信息,这些信息最初都是由服务器通过HTTP响应报文发送的。每当客户端向服务器发送一个HTTP请求是,cookie都会随着请求被一同发送至服务器。
cookie的设计本意是要克服HTTP的无状态性,虽然cookie并不是完成这以目的的唯一方法,但确是最常用的也最流行的方法之一。
cookie在Go语言中用Cookie结构表示。
type Cookie struct{
Name string
Value string
Path string
Domain string
Expiree time.Time
RawExpires string
MaxAge int
Sevure bool
HttpOnly bool
Raw string
Unparsed []string
}
没有设置Expires字段的cookie通常被称为会话cookie或者临时cookie,这种cookie在浏览器关闭的时候就会自动被移除。相对而言,设置了Expires字段的cookie通常被称为持久cookie,这种cookie会一致存在,直到指定的过期时间来临或者瘪手动删除。
Expires字段和MaxAge字段都可以用于设置cookie的过期时间,其中Expires字段用于明确地指定cookie应该在什么时候过期,而MaxAge字段则指明了cookie在被浏览器创建出来之后能存活多少秒。
将cookie发送至客户端
func setCookie(w http.ResponseWrite,r *http.Request){
c := http.Cookie{
Name : "zxt",
value: "GO web",
HttpOnlt: "true",
}
w.Header().Set("Set-Cookie",c.String())
// http.SetCookie(w,&c)
}
从客户端中读取cookie
func getCookie(w hhtp.ResponseWrite, r *http.Request){
h := r.Header["Cookie"]
// c1,err :=r.Cookie("cookie.Name")
fmt.Fprintln(w,h)
}
内容展示
模板引擎
模板引擎通过将数据和模板组合在一起生成最终的HTML,而处理器则复制调用模板引擎并将引擎生成HTML返回给客户端。
WEB模板引擎演变自SSI(服务器端包含)技术,并最终衍生出了PHP,JSP这样的web变成语言,这种演变导致的一个结果是模板引擎并没有相应的标准,并且对各个因为不同原因创造出来的模板引擎来说,特性也是不一样的。不过模板引擎可以分成两大类。
- 无逻辑模板引擎: 将模板中指定的占位符替换成相应的动态数据。这种模板引擎值进行字符串替换,而不执行任何逻辑处理。无逻辑模板引擎的目的是完全分离程序的表现和逻辑,并将所有计算方面的工作都借给处理器完成。
- 嵌入逻辑的模板引擎:将编程语言代码嵌入模板当中,并在模板引擎渲染模板是,由模板引擎执行这些代码并进行相应的字符串替换工作。因为拥有在模板里嵌入逻辑的能力,所以这类模板引擎能够变得非常强大。但是这种能力也会导致逻辑分散遍布在不同的处理器之间,使代码变得难以维护。
Go的模板引擎
Go语言的模板引擎也是介于无逻辑模板引擎和嵌入逻辑模板引擎之间的一种模板引擎。在Web应用里,模板引擎通常由处理器负责触发。
Go模板引擎流程:
处理器首先低啊用模板引擎,截止以模板文件列表的方式向模板引擎传入一个或多个模板,然后在传入模板需要用的动态数据;模板引擎在接收到这些参数之后会生成相应的HTML,并将这些文件写入到ResponseWriter里面,然后由ResponseWriter将HTTP响应返回给客户端。
Go的模板都是文本文档(其中Web应用的模板通常是HTML),它们都嵌入了一些称为动作(action)的指令。从模板引擎的角度来说,模板就是嵌入了的文本(这些文本通常包含在模版文件里面),而模版引擎则通过分析并执行这些文本来生成另一些文本。
Go语言有通用的模板引擎库text/template,它可以处理任意格式的文本,Go语言专门为HTML格式而设计的模板引擎可html/template。模板中的动作默认使用两个{{和}}包围。
简单模板
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html";charset="utf-8">
<title>Go web</title>
</head>
<body>
{{.}}
</body>
</html>
用户可以拥有任意多个模板文件,并且这些模板文件可以使用任意后缀名,但它们的类型必须是可读的文本格式。因为商密昂这段模板的输出的将是一个HTML文件,所以使用了.html作为模板文件的后缀名。
模板中被两个大括号包围的店是一个动作,它指示模板引擎在执行模式时,使用一个值去替换这个动作本身。
Go的Web模板引擎需要以下两个步骤:
- 对文本格式的模板源进行语法分析,创建一个经过语法分析的模板结构,其中模板源即可以是一个字符串,也可以是模板文件中包含的内容。
- 执行经过语法分析的模板,将ResponseWriter和模板所需的动态数据传递给模板引擎,被调用的模板引擎会吧经过语法分析的模板和传入的数据结合起来,生成最终的HTML,并将这些HTML传递给ResponseWriter。
func process(w http.ResponseWriter ,r *http.Pequest){
t,_ := template.ParesFiles("test.html") // 对模板进行语法分析
// t := template.Must(template.PatseFiles("test.html"))
// Must函数可以包裹起一个函数,被包裹的函数会返回一个指向模本的指针和一个错误,如果这个错误不是nil,那么Must函数将产生一个panic。
t.Execute(w,"hello world") //传入数据 执行模板
}
动作
Go模板的动作就是嵌入在模板里面的命令,这些命令在模板中使用两个{{和}}进行包围。Go语言拥有一套非常丰富的动作集合。主要分为以下动作:
- 条件动作
- 迭代动作
- 设置动作
- 包含动作
条件动作
条件动作会根据参数的值来决定对多条语句中的那一条语句进行求值。最简单的条件动作的格式如下:
{{ if arg}}
some content
{{end}}
{{if arg }}
some content
{{ else}}
orther
{{ end }}
以上两种格式中的arg都是传递给条件动作的参数。参数看作一个值,这个值可以是一个字符串常量,一个变量,一个返回单个值得函数或者方法。
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
}
http.HandleFunc("/zxt", zxt)
server.ListenAndServe()
}
func zxt(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("/Users/zhuxiatong/go/src/first_webapp/html/Response.html")
rand.Seed(time.Now().Unix())
i := rand.Intn(10)
println(i)
t.Execute(w, i > 5)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>Go web</title>
</head>
<body>
{{ if .}}
Number is greater than 5 !
{{ else }}
Number is 5 or less!
{{ end }}
</body>
</html>
迭代动作
迭代动作可以对数组,切片,映射或者通道进行迭代,而在迭代循环的内部,点(.)则会被设置为当前被迭代的元素.
{{ range array}}
Dot is set to the element {{.}}
{{end}}
<body>
<ul>
{{range .}}
<li>{{ . }}</li>
{{ else}}
<li> Nothing to show</li>
{{ end }}
</ul>
</body>
func zxt(w http.ResponseWriter ,r *http.Request){
t,_ := template.ParseFiles("/Users/zhuxiatong/go/src/first_webapp/html/Response.html")
daysOfWeek := []string{"Mon","Tue","Wed","Thu","Fri","Sat","Sun"}
e.Execute(w,daysOfWeek)
}
设置动作
设置动作允许用户在指定的范围之内为点(.)设置值。
{{ with arg }}
Dot is set to arg
{{ end }}
{{ with arg }}
Dot is set to arg
{{ else }}
Fallback if arg is empty
{{ end }}
介于{{ with arg }}和{{ end }} 之间的点将被设置为参数arg的值。
包含动作
包含动作允许用户在一个模板里面包含另一个模板,从而构建出嵌套的模板,包含动作的格式为{{ template “name” }}。
如果用户在创建模板的时候没有为模板指定名字,那么Go语言在命名模板时将沿用模板文件的名字及扩展名。
在执行嵌套模板时,必须对涉及的所有模板文件都进行语法分析。忘记对必要的模板文件进行语法分析将导致程序出现不正确的结果。
t1.html
<body>
{{ template " t2.html"}}
</body>
t2.html
<div>
{{.}}
</div>
func zxt(w http.ResponseWriter, r *http.Request){
t,_ := templet.ParseFiles("t1.html","t2.html")
t.Execute(w,"Hello World")
}
参数,变量和管道
一个参数就是模板中的一个值。它可以是布尔值,整数,字符串等字面量,也可以是结构,结构中的一个字段或者数组中的一个键。参数更可以是一个变量,一个方法(这个方法必须只返回一个值和一个错误)或者一个函数。最后,参数也可以是一个点(.),用于表示处理器向模板引擎传递的数据。
{{ if arg }} //arg 就是参数
some content
{{ end }}
用户还可以在动作中设置变量。变量以美元符号($)开头,
$variable := value
{{ range $key, $value := .}}
the key is {{ $key }} and the value is {{ $value }}
{{ end }}
点(.)是个映射,而动作range在迭代这个映射的时候,会将变量 k e y 和 key和 key和value分别初始化为当前被迭代映射元素的键和值。
模板中的管道(pipeline)是多个有序地串联起来的参数,函数和方法,他的工作方式和语法跟Unix的管道也非常相似:
{{p1|p2|p3}}
这里的p1,p2,p3可以是参数或者函数。管道允许用户讲一个参数的输出传递给下一个参数,而各个参数之间则使用|分割
{{ 12.3 | printf "%.2f" }}
函数
Go函数也可用作模板的参数:Go模板引擎内置了一些非常基础的函数,其中包括fmt.Sprint的不同变种创建的几个别名函数,并且用户不仅可以使用模板引擎内置的函数,还可以自行定义函数。
Go模板引擎函数都是受限制的:尽管这些函数可以接受任意多个参数作为输入,但是他们只能返回一个值,或者返回一个值和一个错误。
创建一个自定义模板函数,需要:
- 创建一个名为FuncMap的映射,并将映射的键设置为函数的名字,而映射的值则设置为实际定义的函数。
- 将FuncMap与模板进行绑定。
func formatDate(t time.Time) string{ //定义自定函数
layout := "2006-01-02"
return t.Format(layout)
}
func zxt(w hhtp.ResponseWriter , r *http.Request){
funcMap := template.FuncMap{"fdate" : formarDate } //创建函数映射
t := template.New("tmpl.html").Funcs(funcMap) // 将映射与模板进行绑定
t ,_ := t.ParseFiles("tmpl.html") // 将模板进行语法分析
t.Execute(w,time.Now())
}
模板中的调用
<div> The date is {{ . | fdate }} </div>
<div>
The date is {{fadte . }}
</div>
上下文感知
Go语言模板引擎拥有一个特性-可以根据内容所处的上下文改变其显示的内容。
上下文感知就是对呗显示的内容实施正确的转义。这意味着,如果模板显示的HTML格式的内容,那么模板经对其实施HTML转义;如果模板显示的是JavaScript格式的内容,那么模板将对其实施JavaScript转义。
上下文感知特性主要用于实现动的防御编程,并且它使用起来非常方便。通过根据上下文对内容进行修改。Go模板可以防止某些明显并且低级的编程错误。比如XSS攻击。
防御XSS攻击
持久性XSS漏洞是一种常见的XSS攻击方式,这种攻击是由于服务器将攻击者存储数据原本地显示给其他用户所致的。
func zxt(w http.ResponseWriter , r *http.Request){
t,_ := template.ParseFiles("tmpl.html")
t.Execute(w,temlate.HTML(r.FormValue("comment"))) //将表单的数据转换成HTML
}
参考资料:
《Go Web 编程》 郑兆雄