浅析Golang Http设置api请求路径原理


前言

现如今的应用都提倡的是前后端分离,本文就前后端关联必备的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函数处理。逻辑线很简单,当然中间的细节处理还是有点的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值