Go语言基础之19--web编程基础
一、web编程基础
1.1 web工作方式
1.2 HTTP协议详解
a、http 请求包体
GET /domains/example/ HTTP/1.1 //请求行: 请求方法 请求URI HTTP协议/协议版本 Host:www.iana.org //服务端的主机名 User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //浏览器信息 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客户 端能接收的MIME Accept-Encoding:gzip,deflate,sdch //是否支持流压缩 Accept-Charset:UTF-8,*;q=0.5 //客户端字符编码集 //空行,用于分割请求头和消息体 //消息体,请求资源参数,例如POST传递的参数 |
b、http 响应包体
HTTP/1.1 200 OK //状态行 Server: nginx/1.0.8 //服务器使用的WEB软件名及版本 Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //发送时间 Content-Type: text/html //服务器发送信息的类型 Transfer-Encoding: chunked //表示发送HTTP包是分段发的 Connection: keep-alive //保持连接状态 Content-Length: 90 //主体内容长度 //空行 用来分割消息头和主体 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //消息体 |
1.3 http Keep-Alive特性
A. Keep-Alive用来保持连接
B. Keep-Alive通过web服务器(nginx、httpd)进行设置,保持的时间
1.4 web程序开发
A. 标准包 “net/http”封装web服务相关功能
B. 使用简单、性能媲美nginx。
实例:
package main import ( "fmt" "log" "net/http" "strings" ) func sayhelloName(w http.ResponseWriter, r *http.Request) { //第一个参数是一个接口,第二个参数是一个结构体(封装了一系列方法,比如:取post提交参数等),r是接受请求,w是返回给浏览器 r.ParseForm() //解析参数,默认是不会解析的,如果是post请求(带表单参数),会进行解析 fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息,将表单打印出来 fmt.Println("path", r.URL.Path) //请求路径 fmt.Println("scheme", r.URL.Scheme) //scheme是http还是https fmt.Println(r.Form["url_long"]) //传递了一个表单参数url_long for k, v := range r.Form { fmt.Println("key:", k) fmt.Println("val:", strings.Join(v, "")) } fmt.Fprintf(w, "Hello world!") //这个写入到w的是输出到客户端的 //w这里是一个接口,接口的好处就体现在封装成一个接口的话,不论是返回文件或者图片或者html都可以通过接口进行返回,不用接口的话,你需要为每一种返回类型写函数。帮你更好理解 } func main() { http.HandleFunc("/", sayhelloName) //设置访问的路由,sayhelloName是函数类型的参数 err := http.ListenAndServe(":9090", nil) //设置监听的端口 if err != nil { log.Fatal("ListenAndServe: ", err) } }
执行结果:
浏览器端:
实例:获取用户传递过来的参数
package main import ( "fmt" "net/http" ) func greet(w http.ResponseWriter, r *http.Request) { word := r.FormValue("word") //通过formvalue来获取用户传递过来参数 fmt.Fprintf(w, "greet Hello World! word:%s", word) } func main() { http.HandleFunc("/index", greet) http.ListenAndServe(":8080", nil) }
执行结果:
浏览器端:
1.5 Golang web服务工作方式
图1:
go底层源码就是我们上面图示的体现:
绑定监听端口:
进入服务阶段(死循环,所以不停服务):
客户端来一个请求连接,就accept起一个,完了最后通过起一个协程来处理当前连接的这个请求。
go语言里面网络编程模型很简单,就是过来一个连接,起一个goroutine去处理这个连接请求。如果是nginx、c++、c等就需要做一个异步的epoll处理模型,go就非常简单,源码就和上面一样,所以以后要写一个tcp服务器,将上面源码搬过去就可以用。
图2为图1的精细化展示:
二、表单提交
A. html代码中通过<form> </form>括起来的区域,允许用户提交数据。
B. Go对于表单处理非常方便
实例:
目录结构:
login.html:
<html> <body> <form action="/user/login" method="POST"> <div> <span>用户名:</span><input name="username" > </div> <div> <span>密码:</span><input name="password" > </div> <div> <input type="submit" value="提交"> </div> </form> </body> </html>
main.go
package main import ( "fmt" "io/ioutil" "log" "net/http" ) func userLogin(w http.ResponseWriter, r *http.Request) { fmt.Printf("r.method:%s\n", r.Method) if r.Method == "GET" { //如果是get直接返回请求页面 data, err := ioutil.ReadFile("./login.html") if err != nil { http.Redirect(w, r, "/404.html", http.StatusNotFound) return } w.Write(data) //要把内容输出到浏览器 } else if r.Method == "POST" { r.ParseForm() //解析表单 username := r.FormValue("username") //和html文件要一一对应 password := r.FormValue("password") if username == "admin" && password == "admin" { fmt.Fprintf(w, "login success") } else { fmt.Fprintf(w, "login failed") } } } func main() { http.HandleFunc("/user/login", userLogin) err := http.ListenAndServe(":9090", nil) if err != nil { log.Fatal("ListenAndServe: ", err) } }
执行结果:
浏览器进行测试:
输入正确:
输入错误:
三、模板介绍与使用
3.1 模板替换
A. {{}}(模板变量)来包含需要在渲染时被替换的字段, {{.}}表示当前的对象(go源码传递过来的对象)。
B. 通过{{.FieldName}}访问对象的属性。
实例:
目录结构:
index.html
<html> <body> <form action="/user/login" method="POST"> <div> <span>用户名:{{.Name}}</span> </div> <div> <span>年龄:{{.Age}}</span> </div> </form> </body> </html>
main.go
package main import ( "fmt" "html/template" //加载模板的包 "net/http" ) var ( t *template.Template //定义一个接收模板的全局变量 ) type User struct { Name string Age int } func initTemplate() (err error) { t, err = template.ParseFiles("./index.html") //把模板加载进来 ,t是返回的实例 if err != nil { fmt.Printf("load template failed,err:%v\n", err) return } return } func handleUserInfo(w http.ResponseWriter, r *http.Request) { var user User = User{ //初始化 //可以传递任何类型的数据进去 Name: "user01", Age: 10, } t.Execute(w, user) //进行渲染,t对应的就是模板中的. } func main() { err := initTemplate() if err != nil { return } http.HandleFunc("/user/info", handleUserInfo) err = http.ListenAndServe(":8080", nil) if err != nil { fmt.Printf("listen failed, err:%v\n", err) return } }
执行结果:
浏览器端:
3.2 if判断
比如说一个场景,大于18岁就弹出来一个红色图标,小于18岁就弹出一个灰色图标,我们就可以通过if判断来实现。
模板:
<html> </head> </head> <body> {{if gt .Age 18}} <p>hello, old man, {{.Name}}</p> {{else}} <p>hello,young man, {{.Name}}</p> {{end}} </body> </html>
其它用法:
1) not 非
{{if not .condition}}
{{end}}
2) and 与
{{if and .condition1 .condition2}}
{{end}}
3) or 或
{{if or .condition1 .condition2}}
{{end}}
4) eq 等于
{{if eq .var1 .var2}}
{{end}}
5) ne 不等于
{{if ne .var1 .var2}}
{{end}}
6) lt 小于 (less than)
{{if lt .var1 .var2}}
{{end}}
7) le 小于等于
{{if le .var1 .var2}}
{{end}}
8) gt 大于
{{if gt .var1 .var2}}
{{end}}
9) ge 大于等于
{{if ge .var1 .var2}}
{{end}}
实例:
index.html
<html> <body> <form action="/user/login" method="POST"> <div> <span>用户名{{.Name}}</span> </div> <div> <span>年龄:{{.Age}}</span> </div> {{if gt .Age 18}} <div> <span>hello, old man</span> </div> {{else}} <div> <span>hello, young man</span> </div> {{end}} </form> </body> </html>
main.go代码依然用的是模板替换的代码
查看结果:
解释:
可以看到年龄小于18,所以传递过来是小于18分支的字符串。
3.3 with语法
当我们传递的是结构体,单层没问题,如果是想调用嵌套结构体的话,就会出问题,此时我们就需要用到with语法。
模板:
<html> </head> </head> <body> {{with .Name}} <p>hello, old man, {{.}}</p> {{end}} </body> </html>
解释:
{{.}}:双括号中的点就指的是with后面的变量Name
实例:
结构:
index.html
<html> <body> <form action="/user/login" method="POST"> <div> <span>用户名{{.Name}}</span> </div> <div> <span>年龄:{{.Age}}</span> </div> {{if gt .Age 18}} <div> <span>hello, old man</span> </div> {{else}} <div> <span>hello, young man</span> </div> {{end}} {{with .Address}} <div> <span>省:{{.Province}}</span> </div> <div> <span>城市:{{.City}}</span> </div> <div> <span>邮编:{{.Code}}</span> </div> {{end}} </form> </body> </html>
main.go
package main import ( "fmt" "html/template" "net/http" ) var ( t *template.Template ) type Address struct { City string Province string Code string } type User struct { Name string Age int Address Address //继承 } func initTemplate() (err error) { t, err = template.ParseFiles("./index.html") if err != nil { fmt.Printf("load template failed,err:%v\n", err) return } return } func handleUserInfo(w http.ResponseWriter, r *http.Request) { var user User = User{ //初始化 Name: "user01", Age: 10, Address: Address{ City: "beijing", Province: "beijing", Code: "10086", }, } t.Execute(w, user) } func main() { err := initTemplate() if err != nil { return } http.HandleFunc("/user/info", handleUserInfo) err = http.ListenAndServe(":8080", nil) if err != nil { fmt.Printf("listen failed, err:%v\n", err) return } }
执行结果:
浏览器端查看:
3.4 循环
传递过来的首先必须要是一个切片
模板:
<html> <head> </head> <body> {{range .}} {{if gt .Age 18}} <p>hello, old man, {{.Name}}</p> {{else}} <p>hello,young man, {{.Name}}</p> {{end}} {{end}} </body> </html>
range . 这个点就是切片中的一个元素
实例:
目录结构:
index.html
<html> <body> <form action="/user/login" method="POST"> {{range .}} <div> <span>用户名{{.Name}}</span> </div> <div> <span>年龄:{{.Age}}</span> </div> {{if gt .Age 18}} <div> <span>hello, old man</span> </div> {{else}} <div> <span>hello, young man</span> </div> {{end}} {{with .Address}} <div> <span>省:{{.Province}}</span> </div> <div> <span>城市:{{.City}}</span> </div> <div> <span>邮编:{{.Code}}</span> </div> <hr> {{end}} {{end}} </form> </body> </html>
main.go
package main import ( "fmt" "html/template" "net/http" ) var ( t *template.Template ) type Address struct { City string Province string Code string } type User struct { Name string Age int Address Address } func initTemplate() (err error) { t, err = template.ParseFiles("./index.html") if err != nil { fmt.Printf("load template failed,err:%v\n", err) return } return } func handleUserInfo(w http.ResponseWriter, r *http.Request) { var users []*User for i := 0; i < 15; i++ { var user User = User{ Name: "user01", Age: 10, Address: Address{ City: "beijing", Province: "beijing", Code: "10086", }, } users = append(users, &user) } t.Execute(w, users) //传递一个切片过去 } func main() { err := initTemplate() if err != nil { return } http.HandleFunc("/user/info", handleUserInfo) err = http.ListenAndServe(":8080", nil) if err != nil { fmt.Printf("listen failed, err:%v\n", err) return } }
执行结果:
浏览器端: