http编程
一、HTTP
1.http编程(http是文本协议,socket是二进制协议)
a.Go原生支持http,import(“net/http”)
b.Go的http服务性能和nginx 比较接近
c. 几行代码就可以实现 一个web服务
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/ //浏览器 信息 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客户端能接收的mine Accept-Encoding:gzip,deflate,sdch //是否支持流压缩 Accept-Charset:UTF-8,*;q=0.5 //客户端字符编码集 //空行, 用于分割请求头和消息体 //消息体,请求资源参数,例如POST传递的参数
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包是分段发的,http/1.0是全部一起发,http/1.1是一段一段的发 Connection: keep-alive //保持连接状态 Content-Length: 90 //主体内容长度 //空行用来分割消息头和主体 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //消息体
发送http请求
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package main import ( "fmt" "io" "net" ) func main() { conn, err := net.Dial("tcp", "www.baidu.com:80") if err != nil { fmt.Println("Error dialing", err.Error()) return } defer conn.Close() msg := "GET / HTTP/1.1\r\n" msg += "Host: www.baidu.com\r\n" msg += "Connection: close\r\n" msg += "\r\n\r\n" _, err = io.WriteString(conn, msg) if err != nil { fmt.Println("write string failed, ", err) return } buf := make([]byte, 4096) for { count, err := conn.Read(buf) if err != nil { break } fmt.Println(string(buf[0:count])) } }
HTTP服务端
package main import ( "fmt" "net/http" ) func Hello(w http.ResponseWriter, r *http.Request) { //r 是请求头信息 fmt.Println("handle hello") fmt.Fprintf(w, "hello ") //将数据返回给客户端 } func main() { http.HandleFunc("/", Hello) err := http.ListenAndServe("0.0.0.0:8880", nil) //检测端口,解析请求,分发请求进行处理 if err != nil { fmt.Println("http listen failed") } }
HTTP客户端
package main import ( "fmt" "io/ioutil" "net/http" ) func main() { res, err := http.Get("https://www.baidu.com/") if err != nil { fmt.Println("get err:", err) return } data, err := ioutil.ReadAll(res.Body) //读取网络链接里面数据 if err != nil { fmt.Println("get data err:", err) return } fmt.Println(string(data)) }
2.http常见请求方法
1)Get请求
2)Post请求
3)Put请求
4)Delete请求
5)Head请求
Head请求头
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package main import ( "fmt" "net/http" ) var url = []string{ "http://www.baidu.com", "http://google.com", "http://taobao.com", } func main() { for _, v := range url { resp, err := http.Head(v) //Head请求时只会返回响应头 if err != nil { fmt.Printf("head %s failed, err:%v\n", v, err) continue } fmt.Printf("head succ, status:%v\n", resp.Status) } }
3.http 常见状态码
http.StatusContinue = 100 //继续访问 http.StatusOK = 200 http.StatusFound = 302 //跳转 http.StatusBadRequest = 400 //发送的请求,协议格式不对 http.StatusUnauthorized =401 //没有授权 http.StatusForbidden = 403 //没有权限访问 http.StatusNotFound = 404 //请求地址不存在 http.StatusInternalServerError = 500 //服务器有问题 502 //有代理,后端服务挂了 504 //后端处理请求超时
4、表单处理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package main import ( "io" "net/http" ) const form =`<html><meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <body><form action="#" method="post" name="bar"> <div> 姓名:<input type="text" name="username1"/></div> <div> 密码:<input type="text" name="password"/></div> <input type="submit" value="登录"/> </form></html></body>` func SimpleServer(w http.ResponseWriter, request *http.Request) { io.WriteString(w, "<h1>hello, world</h1>") //返回h1标签至前端渲染 } func FormServer(w http.ResponseWriter, request *http.Request) { w.Header().Set("Content-Type", "text/html") //设置响应头 w.Header().Set("Server", "Go 1.9.2 Server") w.Header().Set("Niubi", "hahahaha") switch request.Method { case "GET": io.WriteString(w, form) case "POST": request.ParseForm() //解析post请求,才能得到请求内容 io.WriteString(w, request.Form["username"][0]) //go支持文本框name相同 io.WriteString(w, "\n") io.WriteString(w, request.FormValue("username"))//取不到内容返回空 } } func main() { http.HandleFunc("/test1", SimpleServer) http.HandleFunc("/test2", FormServer) if err := http.ListenAndServe(":8088", nil); err != nil { } }
5、panic处理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package main import ( "io" "net/http" "log" ) const form = `<html> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <body><form action="#" method="post" name="bar"> <div> 姓名:<input type="text" name="username1"/></div> <div> 密码:<input type="text" name="password"/></div> <input type="submit" value="登录"/> </form></html></body>` func SimpleServer(w http.ResponseWriter, request *http.Request) { io.WriteString(w, "<h1>hello, world</h1>") } func FormServer(w http.ResponseWriter, request *http.Request) { w.Header().Set("Content-Type", "text/html") w.Header().Set("Server", "Go 1.9.2 Server") w.Header().Set("Niubi", "hahahaha") var p *int *p = 10 //出现panic,go自动处理了异常,线程挂掉,但是进程不会挂掉 switch request.Method { case "GET": io.WriteString(w, form) case "POST": request.ParseForm() io.WriteString(w, request.FormValue("username")) io.WriteString(w, "\n") io.WriteString(w, request.FormValue("password")) } } func main() { http.HandleFunc("/test1", logPanics(SimpleServer) http.HandleFunc("/test2", logPanics(FormServer)) if err := http.ListenAndServe(":8088", nil); err != nil { } } func logPanics(handle http.HandlerFunc) http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) {//返回匿名函数,原型和处理函数一致 defer func() { if x := recover(); x != nil { log.Printf("[%v] caught panic: %v", request.RemoteAddr, x) //捕获错误 } }() handle(writer, request) //执行处理函数 } }
二、模板的使用
1、渲染到终端
模板文件index.html
<html> <head> </head> <body> {{if gt .Age 18}} //gt大于, .指后端传过来的数据集 <p>hello, old man, {{.Name}}</p> {{else}} <p>hello,young man, {{.Name}}</p> {{end}} </body> </html>
package main import ( "fmt" "os" "text/template"
) type Person struct { Name string Age string } func main() { t, err := template.ParseFiles("./index.html") //加载模板 if err != nil { fmt.Println("parse file err:", err) return } p := Person{Name: "Mary", Age: "31"} if err := t.Execute(os.Stdout, p); err != nil{ fmt.Println("There was an error:", err.Error()) } }
2、渲染到前端
index2.html
<html> <head> </head> <body> <p>hello, old man, {{.age}}</p> </body> </html>
package main import ( "fmt" "text/template" "net/http" ) type Person struct { Name string Age int } var ( gtemp *template.Template ) func init() { t, err := template.ParseFiles("./index2.html") if err != nil { fmt.Println("parse file err:", err) return } gtemp = t } func handleUserInfo(w http.ResponseWriter, r *http.Request) { p := &Person{ Name:"mary", Age: 20, } //执行模板渲染 gtemp.Execute(w, p) } func main() { http.HandleFunc("/user_info", handleUserInfo) http.ListenAndServe(":8080", nil) }
三、模板语句
1)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>
2)if常见操作符
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
not 非 {{if not .condition}} {{end}} and 与 {{if and .condition1 .condition2}} {{end}} or 或 {{if or .condition1 .condition2}} {{end}} eq 等于 {{if eq .var1 .var2}} {{end}} ne 不等于 {{if ne .var1 .var2}} {{end}} lt 小于 (less than) {{if lt .var1 .var2}} {{end}} le 小于等于 {{if le .var1 .var2}} {{end}} gt 大于 {{if gt .var1 .var2}} {{end}} ge 大于等于 {{if ge .var1 .var2}} {{end}}
3){{.}}
后端传过来的结构体或者map,都是.xx提取数据
<html> <head> </head> <body> <p>hello, old man, {{.}}</p> </body> </html>
4){{with .Var}}
<html> <head> </head> <body> {{with .Name}} <p>hello, old man, {{.}}</p> //.代表Name的值 {{end}} </body> </html>
5)循环
<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>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<html> <head> </head> <body> <table border="1px"> {{range .}} <tr> {{if gt .Age 18}} <td> {{.Name}}</td> <td> {{.Age}}</td> {{else}} <td> {{.Name}}</td> <td> {{.Age}}</td> {{end}} </tr> {{end}} </table> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package main import ( "fmt" "text/template" "net/http" ) type Person struct { Name string Age int } var ( gtemp *template.Template ) func init() { t, err := template.ParseFiles("./index.html") if err != nil { fmt.Println("parse file err:", err) return } gtemp = t } func handleUserInfo(w http.ResponseWriter, r *http.Request) { var persons []*Person for i := 0; i < 10; i++{ p := &Person{ Name: fmt.Sprintf("stu%d", i), Age: i*10, } persons = append(persons, p) } //执行模板渲染 gtemp.Execute(w, persons) } func main() { http.HandleFunc("/user_info", handleUserInfo) http.ListenAndServe(":8080", nil) }
四、静态文件服务器
文件服务器
package main import ( "net/http" ) func main() { http.Handle("/", http.FileServer(http.Dir("./")) //http.Handle("/static/", http.FileServer(http.Dir("./static/"))) // '/static/'路径默认会加在./static/后面,需要将文件放在/static//static/下面 http.Handle("/media/", http.StripPrefix("/media/", http.FileServer(http.Dir("./media/")))) //去掉默认加在url /media/ http.ListenAndServe(":8080", nil) }
http模板渲染展示
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package main import ( "fmt" "html/template" "net/http" ) var ( gtemp *template.Template ) func init() { t, err := template.ParseFiles("./views/index.html") if err != nil { fmt.Println("parse file err:", err) return } gtemp = t } func handleIndex(w http.ResponseWriter, r *http.Request) { gtemp.Execute(w, nil) } func main() { http.HandleFunc("/index", handleIndex) //http.Handle("/static/", http.FileServer(http.Dir("./static/"))) // '/static/'路径默认会加在./static/后面,需要将文件放在/static//static/下面 http.Handle("/media/", http.StripPrefix("/media/", http.FileServer(http.Dir("./media/")))) //去掉默认加在url /media/ http.ListenAndServe(":8080", nil) }