一、参考博客文章整理
1.0 链接整理集合
- 博客文章代码下载地址
- Go的http包详解
- Go语言经典库使用分析(七)| 高性能可扩展 HTTP 路由 httprouter
- golang自定义路由控制实现(一)
- golang自定义路由控制实现(二)-流式注册接口以及支持RESTFUL
- Golang学习笔记 - 标准库'net/http'的简析及自制简单路由框架
- go web框架的路由分析
- golang实现的简单http路由器,用于学习理解http.
1.1 链接文章整理
Go的http包详解
通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程。
- 首先调用Http.HandleFunc
按顺序做了几件事:
- 调用了DefaultServeMux的HandleFunc
- 调用了DefaultServeMux的Handle
- 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
- 其次调用http.ListenAndServe(":9090", nil)
按顺序做了几件事情:
- 实例化Server
- 调用Server的ListenAndServe()
- 调用net.Listen("tcp", addr)监听端口
- 启动一个for循环,在循环体中Accept请求
- 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
- 读取每个请求的内容w, err := c.readRequest()
- 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
- 调用handler的ServeHttp
- 在这个例子中,下面就进入到DefaultServeMux.ServeHttp
- 根据request选择handler,并且进入到这个handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
- 选择handler:
- A 判断是否有路由能满足这个request(循环遍历ServeMux的muxEntry)
- B 如果有路由满足,调用这个路由handler的ServeHTTP
- C 如果没有路由满足,调用NotFoundHandler的ServeHTTP
二、Golang Web路由组件实现方式
2.0 实现方式分类
- 原生方式
(1)调用http.HandleFunc(2)调用http.ListenAndServe(":8080", nil)
- 路由封装重写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 原生方式实现
//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一个空对象,来实现:
//第一种方式
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方法代码:
func NewMyMux() *MyMux {
return new(MyMux)
}
完整代码
//第二种方式
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详细代码:
//第二种方式代码封装
//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代码:
//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代码:
//第二种方式代码封装
//增加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代码:
//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)功能,提升代码的简洁性,也方便后期其他封装操作
//第二种方式代码封装
//封装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代码详情:
//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格式接口
//第二种方式代码封装
//封装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)
}
//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
}
有疑问加站长微信联系(非本文作者)