你还在背 web 框架的面试题么?
你还在为那些面试题里面的知识点,搞得一头问号么?
你还在为刁钻的面试官问 web 框架发愁么?
…….
这些我都知道,我都知道。
乖,别哭了 (,,´•ω•)ノ
所以,我准备开设一个系列文章,我们自己来动手实现一个 web 框架,以练带学,让你正在去了解 web 框架背后的逻辑。
契机
Go 语言发展至今也有些年头了,对于刚接触 Go web 框架的同学:
第一关就是我该学什么框架?
然后网上一搜,主流的有 beego、gin、echo等,特别多。
于是就站在了十字路口,不知道往哪边走。
如果问我怎么选的话,我肯定不做选择,全学。
啊~~ (别打,别打,疼,我说逗的)
这个得根据你的需求,如果你接下来的项目还是那种单体服务,就上大而全的框架的比如beego,因为他库多、功能全。
如果是前后段分离的项目可以学 gin,因为他小而精。
读过源码么?
过了框架选择这关,有志之士就会开始思考,这框架咋实现的?
或者去工作时,面试官总会问,你读过源码么?
反正就是挺头大的,于是我就想着,要不我们自己也实现一个框架吧。
我们不求他能被推广使用,自己撸一个框架之后,再去面对面试官的调戏,我应该就可以投去不懈的眼神了,然后说:
咯,这是我自己撸了的一个 web 框架。
想象总是美好的,现实是骨感的。
其实自己撸一个 web 框架之后,再去使用别的框架也会更顺手一些。
于是说干就干,我就开了这个系列教程。
声明:
我们这个框架参考了很多 Gin 框架里面的思路,但是我们不会把全部功能都实现。
我们只会把关键的 路由、中间件、模板渲染 这三个核心模块完成,其他附带一些 http 基础、上下文、错误恢复这些的知识点分享,感兴趣的可以在这基础上继续扩展。
HTTP 基础
要想自己写一个 web 框架,一些基础的 http 知识是必须要知道的。
我们先用 go 的标准库来实现一个 http 服务:
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8000", nil)
}
**
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello my path is %q\n", r.URL.Path)
}
然后 go run main.go
即可访问看到返回 hello my path is /
为什么我们还要用框架么?
到这里很多同学肯定就会问了,既然标准库已经能实现 http 服务了,那我们还用框架来干啥?
其实 net/http
只是提供了基础的Web功能,即监听端口,映射静态路由,解析HTTP报文。
但一些 web 开发中简单的需求并不支持,比如:
- 动态路由:例如
hello/:name
,hello/*
这类的规则。 - 鉴权:没有分组/统一鉴权的能力,需要在每个路由映射的handler中实现。
- 模板:没有统一简化的HTML机制。
- …
没有框架的话,我们就需要自己去手动实现这些。
Kun框架
这个教程我们将使用 Go 语言实现一个简单的 Web 框架,起名叫做Kun
,因为我网名是 锟 ,所以就取了一个拼音,我也没去检索网上有没有这个框架。
Go语言内置了 net/http
库,封装了HTTP网络编程的基础的接口,我们实现的Kun
Web 框架也是基于net/http
的。
再回到最初的那段代码:
main 的最后一行,是用来启动 Web 服务的,第一个参数是地址,:8000 表示在 8000 端口监听。
第二个参数则代表处理所有的HTTP请求的实例,我们传的nil
代表使用标准库中的实例处理。
第二个参数,就是我们基于net/http
标准库实现Web框架的入口。
为什么这么说呢?
第二个参数的类型是什么呢?
通过查看net/http
的源码可以发现,Handler
是一个接口,需要实现方法 ServeHTTP ,也就是说,只要传入任何实现了 ServerHTTP 接口的实例,所有的 HTTP 请求,就都交给了该实例处理了。
于是,我们可以基于这个思路改下我们的代码:
// 这是我们的引擎结构体
type Engine struct{}
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/":
fmt.Fprintf(w, "hello my path is %q\n", req.URL.Path)
default:
fmt.Fprintf(w, "404 not found: %s\n", req.URL)
}
}
func main() {
engine := new(Engine)
log.Fatal(http.ListenAndServe(":8000", engine))
}
再次启动,你会发现和之前的效果一样。
现在我们完全接管了http请求过来的处理逻辑,后面的文章我们就可以在这个基础上继续叠加了。我们下一篇文章再见!
原文:https://mp.weixin.qq.com/s/JD8ZHhsJTnG6jPnULIh3gg
暂无评论内容