go語言的http包

#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」
main裏的內容解釋:網站

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

##最簡單的http服務 如今開始,我由簡入深的一步一步介紹net/http包
首先,請先忘記引子裏的http.HandleFunc("/", sayhello),這個要到很後面才提到code

其實要使用http包,一句話就能夠了,代碼以下接口

package main

import "net/http"

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

訪問網頁後會發現,提示的不是「沒法訪問」,而是」頁面沒找到「,說明http已經開始服務了,只是沒有找到頁面
由此能夠看出,訪問什麼路徑顯示什麼網頁 這件事情,和ListenAndServe的第2個參數有關ip

查詢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.ResponseWriterget

把上面「網頁未找到」的代碼改一下,讓你們認識一下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")
}

##ServeMux的白話解釋

若是把http服務想象成一個公司的話 沒有ServeMux的話,就像這個公司只有1我的--老闆,什麼事情都是由老闆來作(本身寫switch) 包括作業務(「/sales」),作賬(「/account」),內勤(「/management」),掃地(「/cleaner」)

後來老闆招了4我的,負責上面的4件事情,那老闆要作的就是根據狀況轉發就是了,好比作業務的事情,本身就不用去跑客戶了,交給銷售經理去作,並給銷售經理一些資源(包括客戶名稱地址什麼的)
上面代碼裏的type b struct{}就是個銷售經理,資源就是w http.ResponseWriter, r *http.Request,老闆要工做的就是ServeMux要作的工做

以上的代碼就作了一層轉發,老闆給銷售經理事情後,銷售經理去跑客戶了,也就結束了

實際生活中,銷售經理不會本身去跑客戶的,他也會把接來的活轉給更下面的業務員
那時,銷售經理本身也變成一個ServeMux了

因此爲了能夠ServeMux套ServeMux,須要改造一下ServeMux的ServeHTTP,改完後能夠嵌套無限層,把要作的事情不斷的細化
到最後要真正作事情的人,只須要關心本身要作的事情就能夠了,本身作本身分內的事,一個大公司就運做起來了

##回到開頭

回到開頭,有讓你們先忘掉http.HandleFunc("/", sayhello) 請先忘記引子裏的http.HandleFunc("/", sayhello),這個要到很後面才提到

http.ListenAndServe(":8080", nil)的第2個參數是nil時 http內部會本身創建一個叫DefaultServeMux的ServeMux,由於這個ServeMux是http本身維護的,若是要向這個ServeMux註冊的話,就要用http.HandleFunc這個方法啦,如今看很簡單吧

相關文章
相關標籤/搜索