当前位置: 首页 > 工具软件 > Negroni > 使用案例 >

Negroni和Gorilla/mux 解析 Golang

彭涵衍
2023-12-01

如有错误欢迎纠正, 有缺漏欢迎补充

参考资料:
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 初始化

初始化 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)
}

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.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传进来的handlerServeHttp(),再执行传进来的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)
}
 类似资料: