前言
上一章,我们创建了一个
Engine的结构体,结构体中的router 路由变量为一个map[string]HandleFunc,为了更清晰的职责分离,我们可以把路由模块分离.本章涉及的知识点
- Context的封装
- Router的拆分
分离router
创建router.go 文件 我们把和路由有关的AddRouter Post Get都拆分出来,并且构建一个Router结构体,用来包装 HandleFunc
package main
type Router struct {
handlers map[string]HandleFunc
}
func newRouter() *Router {
return &Router{handlers: make(map[string]HandleFunc)}
}
func (r *Router) AddRouter(method string, path string, handle HandleFunc) {
key := method + "-" + path
r.handlers[key] = handle
}
func (r *Router) Post(path string, handle HandleFunc) {
r.AddRouter("POST", path, handle)
}
func (r *Router) Get(path string, handle HandleFunc) {
r.AddRouter("GET", path, handle)
}
分离之后的gee.go
package main
import (
"fmt"
"net/http"
)
type HandleFunc http.HandlerFunc
type Engine struct {
router *Router
}
func newEngine() *Engine {
return &Engine{router: newRouter()}
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
key := r.Method + "-" + r.URL.Path
if h, ok := e.router.handlers[key]; ok {
h(w, r)
} else {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "404")
}
}
func (e *Engine) Run(addr string) (err error) {
return http.ListenAndServe(addr, e)
}
可以看到分离之后职责清晰了许多,gee只负责维护handler函数,启动TCP服务.router负责维护路由
创建context
在http.ResponseWriter中我们还要设置请求头,返回码等,这些都是公共能力,我们应该抽取出来.
创建context.go
package main
import (
"fmt"
"net/http"
)
type Context struct {
Writer http.ResponseWriter
Req *http.Request
Method string
Path string
StatusCode int
}
func newContext(w http.ResponseWriter, r *http.Request) *Context {
return &Context{
Writer: w,
Req: r,
Method: r.Method,
Path: r.URL.Path,
}
}
func (c *Context) SetHeader(key string, value string) {
c.Writer.Header().Set(key, value)
}
func (c *Context) String(code int, format string, values ...interface{}) {
c.Status(code)
c.SetHeader("Content-Type", "text/plain")
c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}
func (c *Context) Status(code int) {
c.Writer.WriteHeader(code)
c.StatusCode = code
}
func (c *Context) HTML(code int, html string) {
c.Status(code)
c.SetHeader("Content-Type", "text/plain")
c.Writer.Write([]byte(html))
}
接着我们改造HandleFunc为Context
type HandleFunc func(ctx *Context)
//此时的router.go中Handle也需要改动
func (r *Router) Handle(c *Context) {
key := c.Method + "-" + c.Path
if h, ok := r.handlers[key]; ok {
h(c)
} else {
c.String(http.StatusNotFound, "404 page not find %s\n", c.Path)
}
}
//此时main函数中为
e.router.Get("/", func(ctx *Context) {
ctx.HTML(200, "index 首页")
})
这样调用变得特别的简单了.
总结
本节我们主要是路由进行了分离,并且增加了context上下文的封装,使得项目更加易于维护和扩展.