从零实现gin day02

78 阅读2分钟

前言

上一章,我们创建了一个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))  
}  

接着我们改造HandleFuncContext

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上下文的封装,使得项目更加易于维护和扩展.