Golang学习笔记 - 标准库"net/http"的简析及自制简单路由框架

前端之家收集整理的这篇文章主要介绍了Golang学习笔记 - 标准库"net/http"的简析及自制简单路由框架前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

原文链接http://targetliu.com/golang-http-router/

还是在继续学习Go的路上,曾经在使用PHP的时候吃过过度依赖框架的亏。现在学习Go的时候决定先打好基础,从标准库学起走。

源码分析

我们知道最简单的建立http服务器代码基本上都是这样的:

  1. http.HandleFunc('/',func(w http.ResponseWriter,r *http.Request){
  2. fmt.Fprint(w,"Hello world")
  3. })
  4. http.ListenAndServe(":8080",nil)

这样就成功的建立了一个监听 8080 端口的http服务器,当访问的时候输出 Hello world

我们顺藤摸瓜来看看 HandleFunc 做了些什么事:

  1. func HandleFunc(pattern string,handler func(ResponseWriter,*Request)) {
  2. DefaultServeMux.HandleFunc(pattern,handler)
  3. }

这里继续通过调用 DefaultServeMuxHandleFunc 方法注册路由,这个 DefaultServeMux 又是何方圣神:

  1. type ServeMux struct {
  2. mu sync.RWMutex
  3. m map[string]muxEntry
  4. hosts bool // whether any patterns contain hostnames
  5. }
  6.  
  7. type muxEntry struct {
  8. explicit bool
  9. h Handler
  10. pattern string
  11. }
  12.  
  13. // NewServeMux allocates and returns a new ServeMux.
  14. func NewServeMux() *ServeMux { return new(ServeMux) }
  15.  
  16. // DefaultServeMux is the default ServeMux used by Serve.
  17. var DefaultServeMux = &defaultServeMux
  18.  
  19. var defaultServeMux ServeMux

DefaultServeMuxnet/http 包提供的一个默认的 ServeMux 类型,ServeMux 实现了 Handler 接口。

追根究底,发现http服务器收到一条请求后通过 go c.serve(ctx) 开启goroutine 处理这个请求,在这个过程中调用Handler 接口函数 ServeHTTP 来做进一步的处理(比如匹配方法链接等等)。

所以,我们就可以理解 ServeMux 就是 net/http 一个内置的路由功能

继续回到 HandleFunc 来:

  1. func (mux *ServeMux) HandleFunc(pattern string,*Request)) {
  2. mux.Handle(pattern,HandlerFunc(handler))
  3. }

ServeMuxHandleFunc 方法将我们传入的路由具体实现函数转换成 HandlerFunc 类型并通过 Handle 注册到路由。这个 HandlerFunc 类型也实现了 Handler 接口:

  1. type HandlerFunc func(ResponseWriter,*Request)
  2.  
  3. // ServeHTTP calls f(w,r).
  4. func (f HandlerFunc) ServeHTTP(w ResponseWriter,r *Request) {
  5. f(w,r)
  6. }

最后到了 Handle 这个方法Handle 方法通过将 pattern 路径以及实现了 Handler 接口的方法一一对应的保存到 ServeMuxmap[string]muxEntry 中,方便后续请求的时候调用。因此,也可以通过 Handle 直接传入一个实现了 Handler 接口的方法注册路由。

至此,net/http 包中默认路由的注册过程基本上已经走完。

至于请求的时候路由调用,记住通过 ServeHTTP 查找 map 中对应路径并调用相关方法就行了。

自制路由

通过以上的分析,我们可以依样画葫芦,实现自己的路由功能

  1. package route
  2.  
  3. import (
  4. "net/http"
  5. "strings"
  6. )
  7.  
  8. //返回一个Router实例
  9. func NewRouter() *Router {
  10. return new(Router)
  11. }
  12.  
  13. //路由结构体,包含一个记录方法、路径的map
  14. type Router struct {
  15. Route map[string]map[string]http.HandlerFunc
  16. }
  17.  
  18. //实现Handler接口,匹配方法以及路径
  19. func (r *Router) ServeHTTP(w http.ResponseWriter,req *http.Request) {
  20. if h,ok := r.Route[req.Method][req.URL.String()]; ok {
  21. h(w,req)
  22. }
  23. }
  24.  
  25. //根据方法、路径将方法注册到路由
  26. func (r *Router) HandleFunc(method,path string,f http.HandlerFunc) {
  27. method = strings.ToUpper(method)
  28. if r.Route == nil {
  29. r.Route = make(map[string]map[string]http.HandlerFunc)
  30. }
  31. if r.Route[method] == nil {
  32. r.Route[method] = make(map[string]http.HandlerFunc)
  33. }
  34. r.Route[method][path] = f
  35. }

使用:

  1. r := route.NewRouter()
  2. r.HandleFunc("GET","/",r *http.Request) {
  3. fmt.Fprint(w,"Hello Get!")
  4. })
  5. r.HandleFunc("POST","hello POST!")
  6. })
  7. http.ListenAndServe(":8080",r)

这个例子只是依样画葫芦的简单功能实现。

一个完整的路由框架应该包含更复杂的匹配、错误检测等等功能,大家可以试着自己动手试试。

阅读源码和重复造轮子都是学习的方法

最后,欢迎大家关注我的博客http://targetliu.com/

猜你在找的Go相关文章