如有错误欢迎纠正, 有缺漏欢迎补充
参考资料:
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 main
import (
"service"
)
func main() {
server := service.NewServer()
server.Run(":8080")
}
package service
import (
"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.HandlerFunc
mx.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 使用该 Router
n.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.HandlerFunc
m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
初始化 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 middleware
handlers []Handler
}
middleware 的初始化用到了 build 函数,
但首先我们先来理解 middleware 的结构:
middleware 是一个 linked list.
故 negroni.middleware 存的是该 linked list 的头:
type middleware struct {
handler Handler
next *middleware
}
现在可以看 build 函数了
build 函数就是在构建这个 linked list:
func build(handlers []Handler) middleware {
var next middleware
if len(handlers) == 0 {
return voidMiddleware()
} else if len(handlers) > 1 {
next = build(handlers[1:])
} else { // len(handlers) ==1
next = 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)
}
首先用到 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.Handler
MethodNotAllowedHandler http.Handler
parent parentRoute
routes []*Route
namedRoutes map[string]*Route
strictSlash bool
skipClean bool
KeepContext bool
useEncodedPath 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
调用 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)
}