3.06 路由树-静态匹配之集成Server
本节课工程结构如下:
(base) yanglei@yuanhong 11-embeddingRouterIntoServer % tree ./
./
├── context.go
├── handleFunc.go
├── httpServer.go
├── httpServer_test.go
├── node.go
├── router.go
├── router_test.go
└── serverInterface.go
0 directories, 8 filesPART1. 实现HTTPServer.serve()方法
HTTPServer.serve()方法之前的课程中说过,该方法的职责为:查找路由树并执行命中的业务逻辑.我们也已经实现了router.findRoute().
httpServer.go:此处只写做了修改的方法,其他没有改动的方法就不贴了,看着太乱
实现
httpServer.serve()方法:
// serve 查找路由树并执行命中的业务逻辑
func (s *HTTPServer) serve(ctx *Context) {
method := ctx.Req.Method
path := ctx.Req.URL.Path
targetNode, ok := s.findRoute(method, path)
// 没有在路由树中找到对应的路由节点 或 找到了路由节点的处理函数为空(即NPE:none pointer exception 的问题)
// 则返回404
if !ok || targetNode.HandleFunc == nil {
ctx.Resp.WriteHeader(http.StatusNotFound)
// 此处确实会报错 但是作为一个WEB框架 遇上了这种错误也没有特别好的处理办法
// 最多只能是落个日志
_, _ = ctx.Resp.Write([]byte("Not Found"))
return
}
// 执行路由节点的处理函数
targetNode.HandleFunc(*ctx)
}删除
http. ServeHTTP()方法中的panic()
// ServeHTTP WEB框架入口
func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 构建上下文
ctx := &Context{
Req: r,
Resp: w,
}
// 查找路由树并执行命中的业务逻辑
s.serve(ctx)
}PART2. 修改HandleFunc类型的入参
HandleFunc类型的入参2.1 修改入参类型为*Context
*Context之前入参类型为Context
handleFunc.go:
type HandleFunc func(ctx *Context)2.2 修改其他受影响的地方
此处只列出受影响的方法或文件(文件基本上都是测试文件),读者自行比对修改即可
HTTPServer.serve()httpServer_test.gorouter_test.go
PART3. 测试
TODO:文件开头的那句go:build e2e的含义要去查
在httpServer_test.go新创建一个测试函数:
httpServer_test.go:
func TestServer_serve(t *testing.T) {
s := NewHTTPServer()
handleFunc := func(ctx *Context) {
// 直接调用http.ResponseWriter的Write方法时 默认响应码为200
ctx.Resp.Write([]byte("hello order detail"))
}
s.addRoute(http.MethodGet, "/order/detail", handleFunc)
_ = s.Start(":8081")
}其实这里还有一个问题:如果将s := NewHTTPServer()更改为s := &HTTPServer{},就会因为router为空而触发panic.

PART4. 修改HTTPServer的成员属性
HTTPServer的成员属性将*router修改为router
修改原因:就是为了和PPT上的统一一下.这里我看过,GIN的Engine是组合了RouterGroup;而Beego的HttpServer则是组合了*ControllerRegister.倒不是大家都用非指针类型
httpServer.go:
// HTTPServer HTTP服务器
type HTTPServer struct {
router
}router.go:
// newRouter 创建路由森林
func newRouter() router {
return router{
trees: map[string]*node{},
}
}router_test.go中也有修改
至此,静态路由匹配功能完成.将此版本作为v2.
附录
需要课后尝试的内容
将HttpServer结构体修改为私有
这个修改的目的在于:强迫使用者必须使用NewHttpServer()函数来创建HttpServer结构体的实例.否则他自己直接s := &HTTPServer{}触发panic.
何时将成员属性类型定义为指针类型?何时将成员属性类型定义为结构体类型?
以HTTPServer为例:
type HTTPServer struct {
*router
}case1. 使用者用&HTTPServer的情况
&HTTPServer的情况我们期望使用者用HTTPServer的指针,因此其成员属性router是不是指针就不重要了,都行
这里我试过:
type HTTPServer struct {
*router
}
s := &HTTPServer{}
if s.router == nil {
fmt.Printf("s.router is nil\n")
}则打印:s.router is nil
但如果改为:
type HTTPServer struct {
router
}
s := &HTTPServer{}
fmt.Printf("%p\n", &s.router)则打印:0xc00011a050.可以看到已经给router分配了内存
case2. 使用者用HTTPServer的情况
HTTPServer的情况type HTTPServer struct {
*router
}
s := HTTPServer{}
if s.router == nil {
fmt.Printf("s.router is nil\n")
}则打印:s.router is nil
type HTTPServer struct {
router
}
s := HTTPServer{}
fmt.Printf("%p\n", &s.router)则打印:0xc0000aa050.同样已经给router分配了内存.但是!这种方式要考虑值传递的问题.不要忘记GO语言中所有的传递都是值传递!
结论:拿不准就用指针!
Last updated