go語言的http包

jopen 11年前發布 | 70K 次閱讀 Go語言 Google Go/Golang開發

http服務

引子,http的hello world

如果要搜索“go http helloworld”的話,多半會搜索到以下代碼

package main

import (
    "io"
    "net/http"
)

func main() {
    http.HandleFunc("/", sayhello)
    http.ListenAndServe(":8080", nil)
}

func sayhello(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world")
}

這時,如果用瀏覽器訪問localhost:8080的話,可以看到“hello world”
mian里的內容解釋:

  1. 首先注冊一個sayhello函數給“/”,當瀏覽器瀏覽“/”的時候,會調用sayhello函數
  2. 其次開始監聽和服務,“:8080”表示本機所有的ip地址的8080口

最簡單的http服務

現在開始,我由簡入深的一步一步介紹net/http包
首先,請先忘記引子里的http.HandleFunc("/", sayhello),這個要到很后面才提到

其實要使用http包,一句話就可以了,代碼如下

package main

import "net/http"

func main() {
    http.ListenAndServe(":8080", nil)
}

訪問網頁后會發現,提示的不是“無法訪問”,而是”頁面沒找到“,說明http已經開始服務了,只是沒有找到頁面
由此可以看出,訪問什么路徑顯示什么網頁 這件事情,和ListenAndServe的第2個參數有關

查詢ListenAndServe的文檔可知,第2個參數是一個Hander
Hander是啥呢,它是一個接口。這個接口很簡單,只要某個struct有ServeHTTP(http.ResponseWriter, *http.Request)這個方法,那這個struct就自動實現了Hander接口

顯示什么網頁取決于第二個參數Hander,Hander又只有1個ServeHTTP
所以可以證明,顯示什么網頁取決于ServeHTTP

那就ServeHTTP方法,他需要2個參數,一個是http.ResponseWriter,另一個是*http.Request
往http.ResponseWriter寫入什么內容,瀏覽器的網頁源碼就是什么內容
*http.Request里面是封裝了,瀏覽器發過來的請求(包含路徑、瀏覽器類型等等)

認識http.ResponseWriter

把上面“網頁未找到”的代碼改一下,讓大家認識一下http.ResponseWriter
看清楚了,ServeHTTP方法是自己寫的啊,自己去實現

package main

import (
    "io"
    "net/http"
)

type a struct{}

func (*a) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world version 1.")
}

func main() {
    http.ListenAndServe(":8080", &a{})//第2個參數需要實現Hander的struct,a滿足
}

現在
訪問localhost:8080的話,可以看到“hello world version 1.”
訪問localhost:8080/abc的話,可以看到“hello world version 1.”
訪問localhost:8080/123的話,可以看到“hello world version 1.”
事實上訪問任何路徑都是“hello world version 1.”

哦,原來是這樣,當http.ListenAndServe(":8080", &a{})后,開始等待有訪問請求
一旦有訪問請求過來,http包幫我們處理了一系列動作后,最后他會去調用a的ServeHTTP這個方法,并把自己已經處理好的http.ResponseWriter, *http.Request傳進去
而a的ServeHTTP這個方法,拿到*http.ResponseWriter后,并往里面寫東西,客戶端的網頁就顯示出來了

認識*http.Request

現在把上面的代碼再改一下,讓大家認識一下*http.Request

package main

import (
    "io"
    "net/http"
)

type a struct{}

func (*a) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    path := r.URL.String() //獲得訪問的路徑
    io.WriteString(w, path)
}

func main() {
    http.ListenAndServe(":8080", &a{})//第2個參數需要實現Hander接口的struct,a滿足
}

現在
訪問localhost:8080的話,可以看到“/”
訪問localhost:8080/abc的話,可以看到“/abc”
訪問localhost:8080/123的話,可以看到“/123”

最傻的網站

如果再加上一些判斷的話,一個最傻的可運行網站就出來了,如下

package main

import (
    "io"
    "net/http"
)

type a struct{}

func (*a) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    path := r.URL.String()
    switch path {
    case "/":
        io.WriteString(w, "<h1>root</h1><a href=\"abc\">abc</a>")
    case "/abc":
        io.WriteString(w, "<h1>abc</h1><a href=\"/\">root</a>")
    }
}

func main() {
    http.ListenAndServe(":8080", &a{})//第2個參數需要實現Hander接口的struct,a滿足
}

運行后,可以看出,一個case就是一個頁面
如果一個網站有上百個頁面,那是否要上百個case?
很不幸,是的
那管理起來豈不是要累死?
要累死,不過,還好有ServeMux

用ServeMux拯救最傻的網站

現在來介紹ServeMux

ServeMux大致作用是,他有一張map表,map里的key記錄的是r.URL.String(),而value記錄的是一個方法,這個方法和ServeHTTP是一樣的,這個方法有一個別名,叫HandlerFunc
ServeMux還有一個方法名字是Handle,他是用來注冊HandlerFunc
ServeMux還有另一個方法名字是ServeHTTP,這樣ServeMux是實現Handler接口的,否者無法當http.ListenAndServe的第二個參數傳輸

代碼來了

package main

import (
    "net/http"
    "io"
)

type b struct{}

func (*b) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello")
}
func main() {
    mux := http.NewServeMux()
    mux.Handle("/h", &b{})
    http.ListenAndServe(":8080", mux)
}

解釋一下
mux := http.NewServeMux():新建一個ServeMux。
mux.Handle("/", &b{}):注冊路由,把"/"注冊給b這個實現Handler接口的struct,注冊到map表中。
http.ListenAndServe(":8080", mux)第二個參數是mux。
運行時,因為第二個參數是mux,所以http會調用mux的ServeHTTP方法。
ServeHTTP方法執行時,會檢查map表(表里有一條數據,key是“/h”,value是&b{}的ServeHTTP方法)
如果用戶訪問/h的話,mux因為匹配上了,mux的ServeHTTP方法會去調用&b{}的 ServeHTTP方法,從而打印hello
如果用戶訪問/abc的話,mux因為沒有匹配上,從而打印404 page not found

ServeMux就是個二傳手!

ServeMux的HandleFunc方法

發現了沒有,b這個struct僅僅是為了裝一個ServeHTTP而存在,所以能否跳過b呢,ServeMux說:可以 mux.HandleFunc是用來注冊func到map表中的

package main

import (
    "net/http"
    "io"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/h", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "hello")
    })
    mux.HandleFunc("/bye", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "byebye")
    })
    mux.HandleFunc("/hello", sayhello)
    http.ListenAndServe(":8080", mux)
}

func sayhello(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world")
}

回到開頭,有讓大家先忘掉http.HandleFunc("/", sayhello) 請先忘記引子里的http.HandleFunc("/", sayhello),這個要到很后面才提到

http.ListenAndServe(":8080", nil)的第2個參數是nil時
http內部會自己建立一個叫DefaultServeMux的ServeMux,因為這個ServeMux是http自己維護的,如果要向這個ServeMux注冊的話,就要用http.HandleFunc這個方法啦,現在看很簡單吧

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!