# GoHTTP **Repository Path**: codewithyou688/GoHTTP ## Basic Information - **Project Name**: GoHTTP - **Description**: Go语言封装HTTP - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2019-10-22 - **Last Updated**: 2021-12-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #Golang web路由实现方式整理总结 ##一、参考博客文章整理 ### 1.0 链接整理集合 0. [ 博客文章代码下载地址](https://gitee.com/wuhdajun/GoHTTP) 1. [ Go的http包详解](https://www.cntofu.com/book/16/zh/03.4.md) 2. [ Go语言经典库使用分析(七)| 高性能可扩展 HTTP 路由 httprouter](https://www.flysnow.org/2019/01/07/golang-classic-libs-httprouter.html) 3. [ golang自定义路由控制实现(一)](https://www.jianshu.com/p/300f16c2dbd7) 4. [ golang自定义路由控制实现(二)-流式注册接口以及支持RESTFUL](https://www.jianshu.com/p/ec8d2851512f) 5. [ Golang学习笔记 - 标准库'net/http'的简析及自制简单路由框架](https://segmentfault.com/a/1190000006790309) 6. [ go web框架的路由分析](https://blog.csdn.net/cabing2005/article/details/78470036) 7. [ golang实现的简单http路由器,用于学习理解http. ](https://github.com/go-chinese-site/go-simple-router) ### 1.1 链接文章整理 ####[Go的http包详解](https://www.cntofu.com/book/16/zh/03.4.md) 通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程。 - 首先调用Http.HandleFunc 按顺序做了几件事: 1. 调用了DefaultServeMux的HandleFunc 2. 调用了DefaultServeMux的Handle 3. 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则 - 其次调用http.ListenAndServe(":9090", nil) 按顺序做了几件事情: 1. 实例化Server 2. 调用Server的ListenAndServe() 3. 调用net.Listen("tcp", addr)监听端口 4. 启动一个for循环,在循环体中Accept请求 5. 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve() 6. 读取每个请求的内容w, err := c.readRequest() 7. 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux 8. 调用handler的ServeHttp 9. 在这个例子中,下面就进入到DefaultServeMux.ServeHttp 10. 根据request选择handler,并且进入到这个handler的ServeHTTP mux.handler(r).ServeHTTP(w, r) 11. 选择handler: * A 判断是否有路由能满足这个request(循环遍历ServeMux的muxEntry) * B 如果有路由满足,调用这个路由handler的ServeHTTP * C 如果没有路由满足,调用NotFoundHandler的ServeHTTP ##二、Golang Web路由组件实现方式 ### 2.0 实现方式分类 1. 原生方式 (1)调用http.HandleFunc(2)调用http.ListenAndServe(":8080", nil) 2. 路由封装重写ServeHTTP方式 * 路由存储格式: (1)map[string]http.HandlerFunc格式,其中的string由method和传入参数拼接字符串组成(2)map[string]map[string]http.HandlerFunc,,其中一维的键String表示请求method比如post, get 等。二维的键string表示要匹配的URL地址, http.HandlerFunc当然就是处理URL请求的具体方法。 * 重写步骤: ### 2.1 原生方式实现 ```go //http.go package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", SayhelloName) http.ListenAndServe(":8080", nil) } func SayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "原生HTTP路由测试页面") } /* package main import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/", SayhelloName) log.Fatal(http.ListenAndServe(":8080", nil)) //http.ListenAndServe(":8080", nil) } func SayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "原生HTTP路由测试页面") } */ ``` 原生方式主要是2个操作,第1步使用http.HandleFunc函数导入对应的方法,第二步使用http.ListenAndServe监听对应的端口 ### 2.2 路由封装重写ServeHTTP方式实现 ####第1种方式 不存储对应路由规则、直接在ServeHTTP判断对应的路由规则方式 你可以直接使用&结构体方式new一个空对象,来实现: ```go //第一种方式 package main import ( "fmt" "net/http" ) type MyMux struct { } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello MyMuxRoute!") } func main() { mux := &MyMux{} //&结构体方式 http.ListenAndServe(":8080", mux) //mux是核心操作代码 } ``` ####第2种方式 你也可以通过通过一个NewMyMux方法来实现这个功能,然后通过调用当前NewMyMux方法来实现,主流Web框架都使用这种方法实现对应的路由功能,NewMyMux方法代码: ```go func NewMyMux() *MyMux { return new(MyMux) } ``` 完整代码 ```go //第二种方式 package main import ( "fmt" "net/http" ) type MyMux struct { } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello MyMuxRoute!") } //新建的&结构体方法,框架中十分常用 func NewMyMux() *MyMux { return &MyMux{} //等同于 return new(MyMux) } func main() { mux := NewMyMux() //通过调用一个方法来实现 http.ListenAndServe(":8080", mux) //mux是核心代码 } ``` ### 2.3 路由封装重写ServeHTTP方式继续封装代码 #### 我们继续封装代码,将NewMyMux方法和type MyMux struct以及ServeHTTP封装到一个Router包里面,其他的还是在main包里面,03http.go详细代码: ```go //第二种方式代码封装 //03http.go package main import ( "GoHTTP/01httpBase/03httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改 "net/http" ) func main() { mux := route.NewMyMux() //通过调用一个方法来实现 http.ListenAndServe(":8080", mux) //mux是核心代码 } ``` 封装的路由route.go代码: ```go //route包函数封装 //route.go package route import ( "fmt" "net/http" ) type MyMux struct { } //新建的&结构体方法,框架中十分常用 func NewMyMux() *MyMux { return &MyMux{} //等同于 return new(MyMux) } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello MyMuxRoute!") } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { //下面是路由处理,一般web框架会使用一个封装函数统一处理 if r.URL.Path == "/" { sayhelloName(w, r) //这是所有web框架路由的核心代码 return } http.NotFound(w, r) return } ``` #### 我们增加MyMux结构体来存储http.HandleFunc("/", SayhelloName)和重写NewMyMux方法,增加路由的多样性和灵活性,04http.go代码: ```go //第二种方式代码封装 //增加NewMyMux结构体属性,方便存储http.HandleFunc("/", SayhelloName)路由规则 //04http.go package main import ( "GoHTTP/01httpBase/03httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改 "net/http" ) func main() { mux := route.NewMyMux() //通过调用一个方法来实现 http.ListenAndServe(":8080", mux) //mux是核心代码 } ``` route.go代码: ```go //route包函数封装 //route.go package route import ( "fmt" "net/http" ) /*【对比前代码】 type MyMux struct { } */ type MyMux struct { handlers map[string][]*Handler //用于存储http.HandleFunc("/", SayhelloName) 格式的路由规则 } type Handler struct { path string http.HandlerFunc } /*【对比前代码】 //新建的&结构体方法,框架中十分常用 func NewMyMux() *MyMux { return &MyMux{} //等同于 return new(MyMux) } */ func NewMyMux() *MyMux { return &MyMux{make(map[string][]*Handler)} } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello MyMuxRoute!") } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { //下面是路由处理,一般web框架会使用一个封装函数统一处理 if r.URL.Path == "/" { sayhelloName(w, r) //这是所有web框架路由的核心代码 return } http.NotFound(w, r) return } ``` #### 我们继续封装http.ListenAndServe(":8080", mux)功能,提升代码的简洁性,也方便后期其他封装操作 ```go //第二种方式代码封装 //封装http.ListenAndServe(":8080", mux)功能 //05http.go package main import ( "GoHTTP/01httpBase/05httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改 //"fmt" //"net/http" ) func main() { mux := route.NewMyMux() //通过调用一个方法来实现 mux.Listen(":8080") //http.ListenAndServe(":8080", mux) } ``` route.go代码详情: ```go //route包函数封装 //route.go package route import ( "fmt" "log" "net/http" ) type MyMux struct { handlers map[string][]*Handler //用于存储http.HandleFunc("/", SayhelloName) 格式的路由规则 } type Handler struct { path string http.HandlerFunc } //开启http服务 func (m *MyMux) Listen(port string) { err := http.ListenAndServe(port, m) if err != nil { log.Fatal("开启http服务错误!") } } func NewMyMux() *MyMux { return &MyMux{make(map[string][]*Handler)} } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { //下面是路由处理,一般web框架会使用一个封装函数统一处理 if r.URL.Path == "/" { sayhelloName(w, r) //这是所有web框架路由的核心代码 return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello MyMuxRoute!") } ``` #### 我们继续封装存储路由功能和重写ServeHTTP()功能,增加路由多样性和灵活性,方便后期存储Rest格式接口 ```go //第二种方式代码封装 //封装AddRoute添加路由功能、重写ServeHTTP功能 //06http.go package main import ( "GoHTTP/01httpBase/06httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改 "fmt" "net/http" ) func main() { r := route.NewMyMux() //通过调用一个方法来实现 r.AddRoute("GET", "/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello Get!") }) //mux.Listen(":8080") http.ListenAndServe(":8080", r) } ``` ```go //route包函数封装 //route.go package route import ( "fmt" "log" "net/http" "strings" ) type MyMux struct { handlers map[string][]*Handler //用于存储http.HandleFunc("/", SayhelloName) 格式的路由规则 } type Handler struct { path string http.HandlerFunc } //开启http服务 func (m *MyMux) Listen(port string) { err := http.ListenAndServe(port, m) if err != nil { log.Fatal("开启http服务错误!") } } func NewMyMux() *MyMux { return &MyMux{make(map[string][]*Handler)} } //添加路由 func (m *MyMux) AddRoute(mode string, path string, fun http.HandlerFunc) { m.add(mode, path, fun) } //添加路由 /*mode Post|Get|Put|Delete *path 前缀 *fun 方法 */ func (m *MyMux) add(mode, path string, fun http.HandlerFunc) { h := &Handler{ strings.ToLower(path), fun} //fmt调试代码可以删除 fmt.Println("h ::", &h) fmt.Println("strings.ToLower(path) ::", strings.ToLower(path)) fmt.Println("strings.ToLower(mode) ::", strings.ToLower(mode)) //下面是存储路由的核心代码,这里的路由m.handlers存储的格式是Get|Post|Put|Delete:String:http.HandlerFunc m.handlers[strings.ToLower(mode)] = append( m.handlers[strings.ToLower(mode)], h, ) fmt.Println("m.handlers", m.handlers[strings.ToLower(mode)]) } //优化前代码 /* func (p *MyMux) ServeHTTP2(w http.ResponseWriter, r *http.Request) { //下面是路由处理,一般web框架会使用一个封装函数统一处理 if r.URL.Path == "/" { sayhelloName(w, r) //这是所有web框架路由的核心代码 return } http.NotFound(w, r) return } */ //进行路由分配 func (m *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { //处理静态文件 url := strings.Split(strings.TrimLeft(r.URL.Path, "/"), "/") //调试代码,可以直接删除 fmt.Println("r", fmt.Sprintf("%+v", r)) fmt.Println("w", fmt.Sprintf("%+v", w)) for _, handler := range m.handlers[strings.ToLower(r.Method)] { if handler.path == "/"+strings.ToLower(url[0]) { handler.ServeHTTP(w, r) //调用的是func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } return } } http.NotFound(w, r) return } ```