http 路由实现原理
HTTP 路由组件负责将 HTTP 请求交到对应的函数处理(或者是对应的 struct 的方法),路由在框架中相当于一个事件处理器,而这个事件包括:
- 用户请求的路径(path),参数
- HTTP 请求方法(GET|POST|PUT|DELETE|PATCH)
路由器就是根据用户请求的事件信息转发到相应的处理函数(控制层)
默认的路由实现
func fooHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}
http.HandleFunc("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))
在以上的例子中,我们可以看到 调用 http 包中的 DefaultServeMux
来添加路由,需要提供两个参数,第一个参数是 URL,第二个参数是访问此 URL 需要执行的函数,以提供用户访问的资源。
路由的思想主要集中在两点:
-
添加路由的信息
DefultserveMux.Handle(pattern string, handler Handler)
-
根据用户请求转发要执行的函数
路由信息的添加:
在 Go 中,默认的路由添加是通过函数 http.Handle
和 http.HandleFunc
等来添加,底层都是调用了 DefultserveMux.Handle(pattern string, handler Handler)
,这个函数会将路由信息存储在一个 map[string]muxEntry
中。
函数处理:
Go 监听端口,然后接收到 tcp 连接后交给 Handler 来处理,通过 http.DefaultServeMux
函数来进行调度,遍历之前存储的 map 信息,和用户访问的 URL 进行匹配,查询对应注册的处理函数。
for k, v := range mux.m {
if !pathMatch(k, path) {
continue
}
if h == nil || len(k) > n {
n = len(k)
h = v.h
}
}
自定义路由器实现
基于以上的理解,我们可以自定义一个路由处理器:
package main
import (
"fmt"
"net/http"
)
type MyHandle struct {
}
func (handle *MyHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/hello":
sayHello(w,r)
return
case "/hi":
sayHi(w,r)
return
}
http.NotFound(w,r)
return
}
func sayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello Golang!")
}
func sayHi(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi Golang!")
}
func main() {
handle := MyHandle{}
http.ListenAndServe(":9000", &handle)
}
在 终端输入 go run main.go
:
打开浏览器:
![image-20211116232600815](https://i-blog.csdnimg.cn/blog_migrate/e5afe0f3221c6925a7b4319fc32999a5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c559cff231fa79ba1fff2d0952c1405d.png)