Negroni和Gorilla/mux 解析 Golang
如有错误欢迎纠正, 有缺漏欢迎补充
参考资料:
https://github.com/urfave/negroni/blob/master/translations/README_zh_CN.md
https://github.com/gorilla/mux
https://github.com/unrolled/render
https://github.com/pmlpml/golang-learning/tree/master/web
假设我们开发服务端时用如下代码:
package mainimport ("service"
)func main() {server := service.NewServer()server.Run(":8080")
}
package serviceimport ("net/http""github.com/codegangsta/negroni""github.com/gorilla/mux""github.com/unrolled/render"
)// NewServer configures and returns a Server.
func NewServer() *negroni.Negroni {// 返回一个Render实例的指针formatter := render.New(render.Options{IndentJSON: true, // 输出时的格式是方便阅读的JSON})n := negroni.Classic()mx := mux.NewRouter()initRoutes(mx, formatter)n.UseHandler(mx)return n
}func initRoutes(mx *mux.Router, formatter *render.Render) {// 如果用户访问了地址 /hello/{id}, 那就对应调用该函数// 此处的函数为 testHandler 函数返回的 http.HandlerFuncmx.HandleFunc("/hello/{id}", testHandler(formatter)).Methods("GET")
}func testHandler(formatter *render.Render) http.HandlerFunc {return func(w http.ResponseWriter, req *http.Request) {vars := mux.Vars(req)id := vars["id"]formatter.JSON(w, http.StatusOK, struct{ Test string }{"Hello " + id})}
}
negroni和gorilla/mux工作的流程图如下:
server := service.NewServer() // Classic 返回带有默认中间件的Negroni实例指针:n := negroni.Classic() // New 创建 negroni 实例并返回其指针return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))return &Negroni{handlers: handlers,middleware: build(handlers),} // NewRouter 返回一个新的Router实例指针mx := mux.NewRouter()return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} // 为访问的 URL 路径 "/hello/{id}" 匹配一个处理函数mx.HandleFunc("/hello/{id}", http.HandlerFunc(f)).Methods("GET") // 让 negroni 使用该 Routern.UseHandler(mx) // 返回 negroni 实例return n
// 指定监听端口为8080
server.Run(":8080")finalAddr := detectAddress(addr ...) // 返回string // n 就是 negroni 指针, *negroni 实现了ServeHTTP 方法http.ListenAndServe(finalAddr, n) // 此处省略 http.ListenAndServe 的详细流程n.middleware.ServeHTTP(NewResponseWriter(rw), r) // 开始一个一个执行中间件, 第三个参数类型为http.HandlerFuncm.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
Negroni 初始化
初始化 negroni, 首先用到 Classic 函数:
// Classic returns a new Negroni instance with the default middleware already
// in the stack.// 下面这三项都实现了 Handler 接口的 ServeHTTP 方法:
// Recovery - Panic Recovery Middleware
// Logger - Request/Response Logging
// Static - Static File Serving
func Classic() *Negroni {return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}
而 New 函数内容如下, Negroni 实例在此真正被创建:
func New(handlers ...Handler) *Negroni {return &Negroni{handlers: handlers,middleware: build(handlers),}
}
可知 Negroni 的结构:
type Negroni struct {middleware middlewarehandlers []Handler
}
middleware 的初始化用到了 build 函数,
但首先我们先来理解 middleware 的结构:
middleware 是一个 linked list.
故 negroni.middleware 存的是该 linked list 的头:
type middleware struct {handler Handlernext *middleware
}
现在可以看 build 函数了
build 函数就是在构建这个 linked list:
func build(handlers []Handler) middleware {var next middlewareif len(handlers) == 0 {return voidMiddleware()} else if len(handlers) > 1 {next = build(handlers[1:])} else { // len(handlers) ==1next = voidMiddleware()}return middleware{handlers[0], &next}
}
voidMiddleware 函数返回空的 middleware:
func voidMiddleware() middleware {return middleware{HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),&middleware{},}
}
再看回 Negroni 的第二个成员 handlers:
Negroni 使用自己的 Handler 接口, 它实现的 ServeHTTP 方法比 http.Handler 的多了一个 next, 它指向下一个处理函数 http.HandlerFunc:
// Handler handler is an interface that objects can implement to be registered to serve as middleware
// in the Negroni middleware stack.
// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc
// passed in.
//
// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.
type Handler interface {ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
Router 初始化
首先用到 NewRouter 函数:
// NewRouter returns a new router instance.
func NewRouter() *Router {return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
}
我们可以看看 Router 的结构
Router 实现了 http.Handler 接口, Router.namedRoutes 即为每个 path 所对应的不同处理函数(HandlerFunc)
type Router struct {NotFoundHandler http.HandlerMethodNotAllowedHandler http.Handlerparent parentRouteroutes []*RoutenamedRoutes map[string]*RoutestrictSlash boolskipClean boolKeepContext booluseEncodedPath bool
}
接下来为每个 URL path 匹配上一个对应的 Handler, 操作如下:
// HandleFunc registers a new route with a matcher for the URL path.func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,*http.Request)) *Route {return r.NewRoute().Path(path).HandlerFunc(f)}*/
让 negroni 使用该 Router:
// UseHandler adds a http.Handler onto the middleware stack.
// Handlers are invoked in the order they are added to a Negroni.
func (n *Negroni) UseHandler(handler http.Handler) {n.Use(Wrap(handler))
}
再看看 Wrap 函数, 返回一个 negroni 包中的 Handler 给 Use 函数。
可见 negroni 的 HandlerFunc 都是先调用当前 handler 的 ServeHTTP 函数, 再调用传入的 next 函数,而传给 next 的应该就是 negroni 结构中的 middleware.next :
// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni
// middleware. The next http.HandlerFunc is automatically called after the Handler
// is executed.
func Wrap(handler http.Handler) Handler {return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {handler.ServeHTTP(rw, r)next(rw, r)})
}
这里的HandlerFunc
是negroni自定义的, HandlerFunc实现了ServeHttp接口:
// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {h(rw, r, next)
}
所以我们看回Wrap()
函数,它返回一个HandlerFunc
类型,这个类型里面是一个function
,它先执行Wrap
传进来的handler
的ServeHttp()
,再执行传进来的next()
Use 函数, 把 Router 的 handler 放到 middleware 链的末尾:
// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
func (n *Negroni) Use(handler Handler) {if handler == nil {panic("handler cannot be nil")}n.handlers = append(n.handlers, handler)n.middleware = build(n.handlers)
}
回忆文章开头的流程,最后执行了m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
,这个ServeHTTP就是先执行http.Handler.ServeHTTP(rw, r)
,再执行next(rw, r)
,这个next(rw, r)
就是middleware
链表的下一个middleware
监听端口, 处理 Response
调用 negroni 的 Run 函数, 可以看见 Run 函数最后调用了我们熟悉的 http.ListenAndServe 函数:
// Run is a convenience function that runs the negroni stack as an HTTP
// server. The addr string, if provided, takes the same format as http.ListenAndServe.
// If no address is provided but the PORT environment variable is set, the PORT value is used.
// If neither is provided, the address' value will equal the DefaultAddress constant.
func (n *Negroni) Run(addr ...string) {l := log.New(os.Stdout, "[negroni] ", 0)finalAddr := detectAddress(addr...)l.Printf("listening on %s", finalAddr)l.Fatal(http.ListenAndServe(finalAddr, n))
}
既然这样, 那就会调用到 negroni 的 ServeHTTP 函数:
func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {n.middleware.ServeHTTP(NewResponseWriter(rw), r)
} func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {// 开始调用链表中的第一个 handler 的 ServeHTTP 方法m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}
Negroni和Gorilla/mux 解析 Golang相关推荐
- 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析
目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...
- gorilla/mux类库解析
简介 gorilla/mux实现了一个请求路由和分发的Go框架."mux"的意思是"HTTP request multiplexer",和标准包http.Ser ...
- gorilla/mux的使用
github.com/gorilla/mux: golang自带的http.SeverMux路由实现简单,本质是一个map[string]Handler,是请求路径与该路 径对应的处理函数的映射关系. ...
- gorilla/mux 的学习
原文链接:gorilla/mux的学习 源代码: package mainimport ("encoding/json""fmt""github.co ...
- 使用gorilla/mux增强Go HTTP服务器的路由能力
今天这篇文章我们将会为我们之前编写的 HTTP服务器加上复杂路由的功能以及对路由进行分组管理.在之前的文章<深入学习用 Go 编写HTTP服务器>中详细地讲了使用 net/http进行路由 ...
- negroni包和mux包的一点理解
codegangsta/negroni包和gorilla/mux包的一点理解 Negroni是一个http.Handle,因为他实现了 func (n *Negroni) ServeHTTP(rw h ...
- gorilla/mux实现http服务示例
gorilla/mux 小巧玲珑而十分高效,兼容go自带的http.下载源码到本地,编写如下示例: package mainimport ("encoding/json"" ...
- Go 每日一库之 gorilla/mux
简介 gorilla/mux是 gorilla Web 开发工具包中的路由管理库.gorilla Web 开发包是 Go 语言中辅助开发 Web 服务器的工具包.它包括 Web 服务器开发的各个方面, ...
- 路由复用器--gorilla/mux
简介 gorilla/mux是 gorilla Web 开发工具包中的路由管理库.gorilla Web 开发包是 Go 语言中辅助开发 Web 服务器的工具包.它包括 Web 服务器开发的各个方面, ...
最新文章
- R语言使用table函数计算单分类变量的频率表(frequency table)、使用prop.table函数将table函数计算获得的频率表转化为比率表、返回单分类变量每一个类别的比率、或者百分比
- backup ram不稳定 stm32_STM32学习笔记
- centos环境变量设置
- 实现跨浏览器html5表单验证
- form表单提交时,同一个名字的input类型的两个同时提交会覆盖吗
- LocalDateTime - Java处理日期和时间
- json C库源码地址
- java验证jdk_jdk下载,配置,验证
- 读者教育浏览器兼容解决方法
- GNU大型项目构建和覆盖率生成(第一篇)
- 基于51/52单片机毕业设计课题选题表/毕设题目/设计资料
- O2O(online to offline)营销模式
- reStructuredText 表格快速生成
- python做题记录之切西瓜
- 计算机视觉、模式识别、机器学习常用牛人主页链接
- Transphorm第三代经JEDEC认证的GaN半导体将助力稳态光电全新的1.6 kW钛金级ATX PC游戏电源
- 在家如何用手机赚钱,小编来为你一一解答!
- 使Celery 4在Windows上运行的2种方法
- 如何将dxf或dwg等CAD文件与卫星影像地图叠加进行绘图设计?
- R语言绘图patchwork拼图详解快速实现组合图拼接
热门文章
- 虚拟服务器的克隆,怎么克隆远程服务器上的虚拟机
- 山东超级计算机神威,世界最快超级计算机“神威·太湖之光”获得100多项应用成果...
- Blender:导入obj渲染及导出图片+深度图+法向图
- 计算机怎么改磁盘位置,如何修改磁盘0和磁盘1的硬盘位置
- STM32实现自定义HID复合设备
- 2021年华为总监知乎1867赞的Java面试题全集解析助我修行,不吃透感觉都对不起他(上)
- 构建ROP链实现远程栈溢出
- 解决页面favicon.ico文件不存在提示404问题
- 【论文分享】Relation-Aware Graph Attention Network for Visual Question Answering
- 阿里巴巴大数据学院落地成都,计划5年培养2000名高端专业人才