本系列参考于 极客兔兔-7天用Go从零实现Web框架Gee教程,将从零开始实现一个简易的仿 GIN 的框架,称为 Dawn’s Gin 简称 dain。
在本节,最终的代码目录结构如下:
1 2 3 4 5
| dain/ |--dain.go |--go.mod main.go go.mod
|
实现目标
我们最终的实现效果如下,可以看到自实现的框架与 GIN 十分相似:
- 通过
e.GET
和 e.POST
注册路由
- 最后调用
e.RUN
运行 Web 服务器。
main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package main
import ( "DawnGin/dain" "fmt" "net/http" )
func main() { e := dain.New()
e.Get("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World, URL path = %v", r.URL.Path) })
e.Post("/", func(w http.ResponseWriter, r *http.Request) { for k, v := range r.Header { fmt.Fprintf(w, "Header[%q] = %q\n", k, v) } })
e.Run(":9000") }
|
引擎
dain/dain.go
在 GIN 中,添加路由等的操作都是通过引擎 Engine 完成的,所以自定义 Engine。
其中 Engine.router 是一个路由器,类型为 map。key 记录路由注册时的 pattern,value 则为相应的处理逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| type HandlerFunc func(w http.ResponseWriter, r *http.Request)
type Engine struct { router map[string]HandlerFunc }
func New() *Engine { return &Engine{ router: make(map[string]HandlerFunc), } }
|
静态路由注册
我们需要实现通过 POST 和 GET 注册静态路由,首先编写同一的路由注册入口,将路由注册到 Engine.router 中:
1 2 3 4 5
| func (e *Engine) AddRouter(method, pattern string, handler HandlerFunc) { key := method + "-" + pattern e.router[key] = handler }
|
添加对外暴露的与 HTTP Method 相关的路由注册方法,共实现了 GET 和 POST 方法:
1 2 3 4 5 6 7 8 9
| func (e *Engine) Get(pattern string, handler HandlerFunc) { e.AddRouter("GET", pattern, handler) }
func (e *Engine) Post(pattern string, handler HandlerFunc) { e.AddRouter("POST", pattern, handler) }
|
实现 http.Handler 接口
需要使得自定义的路由器工作,首先需要实现 http.Handler 接口,接口定义如下:
1 2 3
| type Handler interface { ServeHTTP(ResponseWriter, *Request) }
|
所以只需要实现 ServeHTTP 方法即可:
1 2 3 4 5 6 7 8 9
| func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) { key := r.Method + "-" + r.URL.Path if handler, ok := e.router[key]; ok { handler(w, r) } else { fmt.Fprintf(w, "404 NOT FOUND FOR PATH: %v", r.URL.Path) } }
|
最后,包装一层执行函数即可:
1 2 3 4
| func (e *Engine) Run(addr string) error { return http.ListenAndServe(addr, e) }
|