Golang http handle詳解

http關(guān)系圖.png

首先通過(guò)一個(gè)函數(shù)啟動(dòng)一個(gè)服務(wù)器客税,只提供一個(gè)方法并返回Hello World!,當(dāng)你在瀏覽器輸入http://127.0.0.1:8080噪漾,就會(huì)看到Hello World亿眠。

對(duì)于http.ListenAndServe來(lái)說(shuō),需要我們提供一個(gè)Addr和一個(gè)Handler会傲,所以當(dāng)我們使用Hello實(shí)現(xiàn)了HandlerServeHTTP方法后锅棕,Hello就會(huì)被認(rèn)為是一個(gè)Handler拙泽,并將其提供給http.ListenAndServe后會(huì)自動(dòng)調(diào)用ServeHTTP方法。

type Hello struct {
}

func (hello Hello) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
    w.Write([]byte("Hello World!"))
}

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

根據(jù)上圖我們看到httpListenAndServe調(diào)用了Server中的ListenAndServe方法裸燎,所以以上代碼可以寫(xiě)成顾瞻,再次使用瀏覽器訪問(wèn)http://127.0.0.1:8080,我們看到了Hello World德绿。

type Hello struct {
}

func (hello Hello) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
    w.Write([]byte("Hello World!"))
}

func main() {
    server := http.Server{Addr: ":8080", Handler: Hello{}}
    server.ListenAndServe()
}

以上的方法我們只能通過(guò)根路徑訪問(wèn)我們想要的結(jié)果荷荤,如何使用不同的路徑訪問(wèn)不同的處理程序呢,你可以寫(xiě)成下面這樣移稳,使用http的Handle方法蕴纳,把路徑和程序關(guān)聯(lián)起來(lái)并注冊(cè)到程序中。使用瀏覽器訪問(wèn)http://127.0.0.1:8080/hello个粱,我們看到了Hello World古毛,那如果訪問(wèn)http://127.0.0.1:8080會(huì)是什么呢,我們看一下都许,是404 page not found稻薇。

type Hello struct {
}

func (hello Hello) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
    w.Write([]byte("Hello World!"))
}

func main() {
    http.Handle("/hello", Hello{})
    http.ListenAndServe(":8080", nil)
}

好的,我們繼續(xù)看httpHandle做了什么胶征,點(diǎn)開(kāi)源碼塞椎,發(fā)現(xiàn)它使用DefaultServeMux調(diào)用了一個(gè)Handle函數(shù)

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

DefaultServeMux是什么,我們繼續(xù)展開(kāi)睛低,發(fā)現(xiàn)DefaultServeMuxServeMux實(shí)例的一個(gè)指針案狠,而且ServeMux是一個(gè)struct,通過(guò)Handle方法把程序路徑和程序進(jìn)行注冊(cè)暇昂。

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern")
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if _, exist := mux.m[pattern]; exist {
        panic("http: multiple registrations for " + pattern)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    e := muxEntry{h: handler, pattern: pattern}
    mux.m[pattern] = e
    if pattern[len(pattern)-1] == '/' {
        mux.es = appendSorted(mux.es, e)
    }

    if pattern[0] != '/' {
        mux.hosts = true
    }
}

繼續(xù)觀察這個(gè)struct莺戒,發(fā)現(xiàn)它實(shí)現(xiàn)了ServeHTTP,那也就是說(shuō)明ServeMux是一個(gè)Handler

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

根據(jù)以上分析發(fā)現(xiàn)DefaultServeMux是一個(gè)默認(rèn)的路由程序急波,它將接收到的不同的程序映射到不同的路徑上等待訪問(wèn),同時(shí)它也是一個(gè)Handler瘪校。

Handler.png

現(xiàn)在我把程序變動(dòng)一下澄暮,這樣也能達(dá)到同樣的效果, 使用瀏覽器訪問(wèn)http://127.0.0.1:8080/hello依然能看到Hello World阱扬。

func main() {
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World!"))
    })
    http.ListenAndServe(":8080", nil)
}

http中的HandleFunc又是什么呢泣懊,我們點(diǎn)進(jìn)去看一下,發(fā)現(xiàn)這個(gè)方法需要傳入func(ResponseWriter, *Request)類型的函數(shù)麻惶。并且最終被傳入了我們前面提到的ServeMuxHandle中馍刮。但是,我們知道只有實(shí)現(xiàn)了ServeHTTP方法才是Handler窃蹋,而ServeMuxHandle需要一個(gè)Handler類型的變量卡啰,顯然這個(gè)handler函數(shù)并不是一個(gè)真正的Handler静稻。

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    mux.Handle(pattern, HandlerFunc(handler))
}

但是我們看到它被強(qiáng)轉(zhuǎn)成了HandlerFunc類型。那么我們繼續(xù)查看HandlerFunc匈辱,HandlerFunc實(shí)現(xiàn)了ServeHTTP振湾,那么這就說(shuō)明HandlerFunc是一個(gè)Handler,所以最終傳入到ServeMux.Handle中的HandlerFunc(handler)是一個(gè)Handler

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

那么我們的代碼也可以寫(xiě)成這樣:

func Hello(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World!"))
}

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

或者這樣:

func Hello(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World!"))
}

func main() {
    http.Handle("/hello",http.HandlerFunc(Hello))
    http.ListenAndServe(":8080", nil)
}

最后http包中還有幾個(gè)內(nèi)置的Handler亡脸,您可以自己去探索:

NotFoundHandler
StripPrefix
RedirectHandler
TimeoutHandler
FileServer
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末押搪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浅碾,更是在濱河造成了極大的恐慌大州,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垂谢,死亡現(xiàn)場(chǎng)離奇詭異厦画,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)埂陆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門苛白,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人焚虱,你說(shuō)我怎么就攤上這事购裙。” “怎么了鹃栽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵躏率,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我民鼓,道長(zhǎng)薇芝,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任丰嘉,我火速辦了婚禮夯到,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饮亏。我一直安慰自己耍贾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布路幸。 她就那樣靜靜地躺著荐开,像睡著了一般。 火紅的嫁衣襯著肌膚如雪简肴。 梳的紋絲不亂的頭發(fā)上晃听,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼能扒。 笑死佣渴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赫粥。 我是一名探鬼主播观话,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼越平!你這毒婦竟也來(lái)了频蛔?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤秦叛,失蹤者是張志新(化名)和其女友劉穎晦溪,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體挣跋,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡三圆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了避咆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舟肉。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖查库,靈堂內(nèi)的尸體忽然破棺而出路媚,到底是詐尸還是另有隱情,我是刑警寧澤樊销,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布整慎,位于F島的核電站,受9級(jí)特大地震影響围苫,放射性物質(zhì)發(fā)生泄漏裤园。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一剂府、第九天 我趴在偏房一處隱蔽的房頂上張望拧揽。 院中可真熱鬧,春花似錦腺占、人聲如沸强法。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至闰歪,卻和暖如春嚎研,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工临扮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留论矾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓杆勇,卻偏偏與公主長(zhǎng)得像贪壳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚜退,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容