Go 原生支持http:import "net/http"
Go 的http服務性能和nginx比較接近:
就是說用Go寫的Web程序上線,程序前面不需要再部署nginx的Web服務器,這裏省掉的是Web服務器。如果服務器上部署了多個Web應用,還是需要反向代理的,一般這也是nginx或apache。
一般幾行代碼就可以實現一個web服務:
package main
import (
"fmt"
"net/http"
)
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Println(*r)
fmt.Fprintf(w, "Hello World")
}
func main() {
http.HandleFunc("/", Hello)
err := http.ListenAndServe("0.0.0.0:8000", nil)
if err != nil {
fmt.Println("http Listen failed")
}
}
http 常見的請求方法:有以下5種方法
1 Get請求 ,2 Post請求,3 Put請求,4 Delete請求,5 Head請求
我們先來看看Get 請求
使用Get請求網站的示例:
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
res, err := http.Get("http://edu.51cto.com")
if err != nil {
fmt.Println("http get ERRPR:", err)
return
}
data, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println("get data ERROR:", err)
return
}
fmt.Println(string(data))
}
Head請求
Head請求只返回響應頭。如果只想要獲取一些狀態信息的話,可以用Head請求。這樣避免返回響應體,響應體的數據是比較多的,適合做監控。Head請求的示例:
package main
import (
"fmt"
"net/http"
)
var urls = []string{
"http://×××w.baidu.com",
"http://×××w.google.com",
"http://×××w.sina.com.cn",
"http://×××w.163.com",
}
func main() {
for _, v := range urls {
resp, err := http.Head(v)
if err != nil {
fmt.Println("Head request ERROR:", err)
continue
}
fmt.Println(resp.Status)
}
}
http 常見狀態碼
http.StatusContinue = 100
http.StatusOK = 200
http.StatusFound = 302 跳轉
http.StatusBadRequest = 400 非法請求
http.StatusUnanthorized = 401 沒有權限
http.StatusForbidden = 403 禁止訪問
http.Status.NotFound = 404 頁面不存在
http.StatusInternalServerError = 500 內部錯誤
處理form表單
package main
import (
"fmt"
"io"
"net/http"
)
const form = `
<html>
<body>
<form action="#" method="post" name="bar">
<input type="text" name="in" />
<input type="text" name="in" />
<input type="submit" value="Submit" />
</form>
</body>
</html>`
func FormServer(w http.ResponseWriter, request *http.Request) {
w.Header().Set("content-Type", "text/html")
switch request.Method {
case "GET":
io.WriteString(w, form)
case "POST":
request.ParseForm()
io.WriteString(w, request.Form["in"][0]) // 注意上面的2個input的name是一樣的
io.WriteString(w, request.Form["in"][1]) // 所以這是一個數組
io.WriteString(w, "</br>")
io.WriteString(w, request.FormValue("in")) // 一般去一個值,就用這個方法
}
}
func main() {
http.HandleFunc("/form", FormServer)
if err := http.ListenAndServe(":8000", nil); err != nil {
fmt.Println("監聽端口ERROR:", err)
}
}
panic 處理
如果處理函數裏有panic,會導致整個程序崩潰,所以要 defer revoer()
來處理 panic。在處理函數開頭defer一個匿名函數:
func FormServer(w http.ResponseWriter, request *http.Request) {
// 增加一個defer來處理panic
defer func() {
if x := recover(); x != nil {
log.Println(request.RemoteAddr, "捕獲到異常:", x)
}
}()
// 原本的處理函數的內容
w.Header().Set("content-Type", "text/html")
switch request.Method {
case "GET":
io.WriteString(w, form)
case "POST":
request.ParseForm()
io.WriteString(w, request.FormValue("in")) // 一般去一個值,就用這個方法
}
// 搞個panic出來
zero := 0
tmp := 1 / zero
io.WriteString(w, string(tmp))
}
優化統一處理
按照上面的做法,要在每個處理函數的開頭都加上panic的處理。由於每個處理函數的panic處理方法都一樣,所以可以寫一個自定義的處理函數:
// 自定義的panic處理的函數
func logPanics(handle http.HandlerFunc) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
defer func() {
if x := recover(); x != nil {
log.Println(request.RemoteAddr, "捕獲到異常:", x)
}
}()
// 上面先處理panic,再接着下面調用業務邏輯
handle(writer, request)
}
}
func main() {
// http.HandleFunc("/form", FormServer) // 修改調用處理函數的方法
http.HandleFunc("/form", logPanics(FormServer)) // 把處理函數傳給自己寫的封裝了panic處理的函數裏
if err := http.ListenAndServe(":8000", nil); err != nil {
fmt.Println("監聽端口ERROR:", err)
}
}
原本直接調用處理函數。現在調用自定義的函數,把處理函數傳進去。在自定義的函數裏先加載defer,然後再調用執行原本的處理函數。邏輯很簡單,就是把處理函數作爲參數傳給自定義的函數,在自定義的函數裏再調用處理函數。在自定義的函數裏寫上defer,這樣就相當於所有的處理函數都有defer了。
模板
使用模板需要用到 "text/template" 包。然後調用模板的t.Execute()方法輸出。
替換
先準備一個簡單的模板:
<p>Hello {{.Name}}</p>
<p>Age: {{.Age}}</p>
然後在Go裏使用模板:
package main
import (
"fmt"
"os"
"text/template"
)
type Person struct {
Name string
Age int
}
func main() {
t, err := template.ParseFiles("index.html")
if err != nil {
fmt.Println("模板解析異常:", err)
return
}
p := Person{"Bob", 32}
if err := t.Execute(os.Stdout, p); err != nil {
fmt.Println("模板加載數據異常:", err)
}
}
/* 執行結果
PS H:\Go\src\go_dev\day10\http\use_template> go run main.go
<p>Hello Bob</p>
<p>Age: 32</p>
PS H:\Go\src\go_dev\day10\http\use_template>
*/
如果直接用 {{.}} 不加字段名的話,就是輸出結構體打印的效果。
輸出到瀏覽器裏
要輸出到瀏覽器裏,只需要在 t.Execute(os.Stdout, p)
裏,把原本輸出到終端換成輸出到處理函數的 w http.ResponseWriter 類型,就好了。
html模板的內容不變,下面是go的代碼:
package main
import (
"fmt"
"net/http"
"text/template"
)
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
type Person struct {
Name string
Age int
}
func Index(w http.ResponseWriter, r *http.Request) {
p := Person{"Cara", 18}
t, err := template.ParseFiles("index.html")
if err != nil {
fmt.Println("加載模板ERROR:", err)
return
}
t.Execute(w, p)
}
func main() {
http.HandleFunc("/", Hello)
http.HandleFunc("/index", Index)
err := http.ListenAndServe("0.0.0.0:8000", nil)
if err != nil {
fmt.Println("http Listen failed")
}
}
判斷
用法示例:
<body>
{{if gt .Age 18}}
<p>已成年</p>
{{else}}
<p>未成年</p>
{{end}}
</body>
更多判斷邏輯:
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 小於
{{if lt .var1 .var2}}
{{end}}
le 小於等於
{{if le .var1 .var2}}
{{end}}
gt 大於
{{if gt .var1 .var2}}
{{end}}
ge 大於等於
{{if ge .var1 .var2}}
{{end}}
with 封裝
with語句就是創建一個封閉的作用域,在其範圍內,{{.}}代表with的變量,而與外面的{{.}}無關,只與with的參數有關:
<body>
{{with .Name}}
<p>{{.}}</p>
{{end}}
</body>
上面這樣包在 {{with .Var}} 裏,with 裏的 {{.}} 代表的就是 Var 這個變量。
with 可以封裝常數:
{{ with "world"}}
Now the dot is set to {{ . }}
{{ end }}
循環(遍歷)
golang的template支持range循環來遍歷map、slice內的內容,在range循環內,還可以使用$設置循環變量,我們可以通過 $i $v 來訪問遍歷的值。語法爲:
{{range $i, $v := .slice}}
<li>key: {{ $key }}, value: {{ $value }}</li>
{{end}}
這是另外一種遍歷方式,這種方式無法訪問到index或者key的值,需要通過點來訪問對應的value:
{{range .slice}}
{{.field}}
{{end}}
在循環內,點是代表遍歷的值。原本使用點來訪問的變量,那麼在循環內部就要用 $. 來訪問。下面的例子表示循環內和循環外 ArticleConten 這個變量訪問的方式:
{{.ArticleContent}}
{{range .slice}}
{{$.ArticleContent}}
{{end}}
定義變量
模板的參數可以是go中的基本數據類型,如字串,數字,布爾值,數組切片或者一個結構體。在模板中設置變量可以使用 $variable := value。我們在range迭代的過程使用了設置變量的方式。
{{$article := "hello"}}
{{$name := .Name}}