一个简单的 web 服务 demo
首先是一个基于 go 的标准库 net/http 的 web 服务的 demo:
1 |
|
如何注册 handler
上面的 demo 中用了三种看似不同的方式注册了三个 handler,但实际上用的都是 net/http
包中的两个函数:
1 |
|
其中,pattern
是请求的路径,也就是 URL,而第二个参数略有不同,从函数的名字可以看出,一个是实现了 Handler
接口的类型,而另一个则是一个函数类型的参数,其函数签名是 func(ResponseWriter, *Request)
。事实上,Handler
接口是这样子的:
1 |
|
而第三种中的 http.HandlerFunc()
实际上就是一个适配器:
1 |
|
所以三种方法本质都是一样的,都会交给 DefaultServeMux
来处理,而 DefaultServeMux
是个 ServerMux
类型,以 key-value 的形式保存了所有的 url-handler 映射
1 |
|
来看一下这个映射是怎么保存(注册)的:
1 |
|
OK,到现在为止,handler 的注册过程基本捋通了,还算简单。那么问题来了,http.ListenAndServe(":8090", nil)
开启 web 服务后,是怎么和上面注册的 handler 关联上的呢??
请求是怎么到 handler 上的
如果我们从 http.ListenAndServe
一路点进去的话,就会发现。。。迷失了,这里稍稍过一下:
1 |
|
1 |
|
这个方法有点长,先只看最关键的地方:
1 |
|
可以看到经典的几个步骤:accept ➡️ 建立新连接 ➡️ 开启一个 gorotine 来处理
这个 c.server()
方法也很长,找到这一行:
1 |
|
1 |
|
好了,到这里,算是关联上了,那么是怎么执行呢?继续往下:
1 |
|
当然,这个里头还是有一定的道道的,这边就不展开了。
从上面的简单分析可以看出,这个 go 自带的默认路由器是真的很简单的,无法满足高级一点的一些需求,比如:
- 按请求方法分类进行路由(GET、POST、PUT、DELETE等);
- 不支持参数设定,例如
/user/:uid
这种泛类型匹配
所以就需要自定义路由。
自定义路由
鉴于上面所说的默认路由器的缺点,这边实现一个简单的自定义路由器,来提供根据请求方法进行路由的功能
1 |
|
跑起来试试:
1 |
|
以上就完成了一个简易的自定义路由。
END