前言
现如今的应用都提倡的是前后端分离,本文就前后端关联必备的api来看看,Golang官方自带的http是怎么实现设置api地址的。
一、使用步骤?
1.设置api路径及函数
代码如下:
http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Hello, %q", html.EscapeString(request.URL.Path))
})
2.监听HTTP
代码如下:
http.ListenAndServe(":8080",nil)
监听中的handle一般使用nil,如果要实现像gin框架那样可以设置api接收类型(GET,POST等)则需要实现该handle。
二、源码追踪
首先明确我们的目标:
源码内部是如何实现根据我们设定的路径去找到对应的HanleFunc的?
猜测:将我们定义的函数以路径为key函数为value保存到内存中
1.如何保存HandleFunc
我们点击http.HandleFunc函数进去可以看到一个DefaultServeMux字段,点击跟踪发现这是个结构体,且指向ServeMux:
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
可以看到该结构体中存在一个map类型的字段,且key为string,看到这里感觉有点猫腻~~继续!!
点击进入该结构体的HandleFunc函数:
// 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))
}
然后再进入Handle函数:
// 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
}
}
到这里可以清除的看到代码将处理函数跟我们定义的api路径放入到一个muxEntry中然后以路径为key,muEntry为value存入到map里面。所以我们的猜测正确~~
2.http.ListenAndServe如何调用保存进map的函数
同样的操作,我们一步一步进入源码,查看哪些代码令人值得怀疑,直到我们发现了下面这个函数鬼头鬼脑的缩在后面:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
DefaultServeMux字段在这里再次被应用,前面我们保存的内容也是属于该结构体,所以到这里真相应该算是浮出水面了~~嗯,看到最后>>
点击ServeHTTP函数发现是个接口:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
再找,肯定是ServeMux实现了该函数,找着找着果然有收获~~
// 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)
}
点击进入mux.Handler函数,最后我们会发现该handler:
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
可以看到match在查找对应的api地址对应的处理函数,拿到处理函数之后再调用我们自己的HandleFunc函数实现一个闭环。
结论
案例:
http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Hello, %q", html.EscapeString(request.URL.Path))
})
程序会先将我们设置的test跟func函数以test为key,func为value保存到map中,最后在请求过来的时候再次去map中查找,查找到了的话则回调给func函数处理。逻辑线很简单,当然中间的细节处理还是有点的。