在追求便利的这条路上,人们是永远不会满足的。
上一篇文章我们只实现了 web 框架中最核心的那十几行代码,但是很显然,在便利性上是不能满足的。
这边文章我们进一步再封装,创建我们的上下文。
这里的上下文不是 go 标准库里面的 context
,而是我们自己去定义的上下文,但是作用和标准库里面的上下文是一样的。
那为什么不使用标准库里面的上下文呢?
先卖个关子,感兴趣可以去读下我们往期关于 标准库Context 的介绍!
这个的定义其实不太好去解释,你可以理解就是承接上下文的载体(变量),他可以被无限的传递下去。
举个例子:
你的朋友突然对你说:“丫的,大傻子”
你肯定以为他在骂你。
但是如果在说这句话之前,他还给你说了,一句:“我刚看到一句台词,真惊艳”
你就知道他后面那句大傻子,不是在说你了。
在程序中,也会出现这种情况,很多时候会出现方法调方法的情况,多次连调,同时下一次的调用还要使用到上次调用的结果。
此时这里就需要一个载体,来承载这些结果,并向下传递。
这就是上下文,也不知道你理解到了没?
我就当你理解了,如果没理解,可以下方给我留言。
首先我们先来看下上一篇文章里面,我们怎么使用 kun 框架的:
func main() {
engine := kun.New()
engine.GET("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
})
engine.Run(":8081")
}
再看下,gin 框架里面:
在 gin 里面,我在方法里面可以直接返回 json 等数据,同时他还能一层一层的往下传递。
再看看我们上一篇文章实现的,我们得自己去设置返回的相应头等数据,用起来就不怎么便利了。
那怎么办?
迭代呀!
首先我们需要创建一个结构体,用来缓存数据,当做传递的载体:
type Context struct {
Writer http.ResponseWriter
Req *http.Request
Path string
Method string
StatusCode int
}
func newContext(writer http.ResponseWriter, req *http.Request) *Context {
return &Context{
Writer: writer,
Req: req,
Path: req.URL.Path,
Method: req.Method,
}
}
我在里面塞入了些,我觉得接下来向下传递的过程中,有用的变量,你也可以根据你的需要进行增删。
然后在为这个载体,添加一些我们常用的方法:
// 设置消息头
func (this *Context) SetHeader(key string,value string) {
this.Writer.Header().Set(key,value)
}
// 设置返回的code
func (this *Context) Status(code int) {
this.StatusCode = code
this.Writer.WriteHeader(code)
}
// 返回字符串
func (this *Context) String(code int, format string, values ...interface{}) {
this.SetHeader("Content-Type", "text/plain")
this.Status(code)
this.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}
我这里就写三个方法就好了,你可以根据自己需要,添加返回 json,html 等入口。
这就是我们的上下文了哈。
我们的载体创建好了,怎么整合到我们的框架里面呢?
首先,我们需要修改上一篇文章中 type HandleFun func(http.ResponseWriter, *http.Request)
的定义,改为 type HandleFun func(*ctx* Context)
,这样我们的上下文就被用上了。
其次修改 ServeHTTP 方法:
//ServeHTTP 关键方法 实现 Handler 的接口
func (this *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
key := req.Method + "-" + req.URL.Path
if handler,ok := this.router[key];ok {
handler(newContext(w,req))
}else{
fmt.Fprintf(w,"404 Not Found: %s\n", req.URL)
}
}
当匹配到路由,调用方法时,我们传入自己定义的上下文。
最后再使用我们框架的地方,修改下回调方法:
engine := kun.New()
engine.GET("/", func(c *kun.Context) {
c.String(200,"path: %s", c.Req.URL.Path)
})
engine.Run(":8081")
这样就可以使用到我们上下文里面的方法了。
到这来我们的上下文就封装完成了,你可以任意扩展你的上下文。
接下来我会开始搞路由部分了,目前的路由匹配方式效率是挺高的,但是并不支持 /hello/:name
这样的动态路由。
最主要他还在核心代码里面,不方便我们扩展。