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

参考资料:
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传进来的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)
}

Negroni和Gorilla/mux 解析 Golang相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. gorilla/mux类库解析

    简介 gorilla/mux实现了一个请求路由和分发的Go框架."mux"的意思是"HTTP request multiplexer",和标准包http.Ser ...

  3. gorilla/mux的使用

    github.com/gorilla/mux: golang自带的http.SeverMux路由实现简单,本质是一个map[string]Handler,是请求路径与该路 径对应的处理函数的映射关系. ...

  4. gorilla/mux 的学习

    原文链接:gorilla/mux的学习 源代码: package mainimport ("encoding/json""fmt""github.co ...

  5. 使用gorilla/mux增强Go HTTP服务器的路由能力

    今天这篇文章我们将会为我们之前编写的 HTTP服务器加上复杂路由的功能以及对路由进行分组管理.在之前的文章<深入学习用 Go 编写HTTP服务器>中详细地讲了使用 net/http进行路由 ...

  6. negroni包和mux包的一点理解

    codegangsta/negroni包和gorilla/mux包的一点理解 Negroni是一个http.Handle,因为他实现了 func (n *Negroni) ServeHTTP(rw h ...

  7. gorilla/mux实现http服务示例

    gorilla/mux 小巧玲珑而十分高效,兼容go自带的http.下载源码到本地,编写如下示例: package mainimport ("encoding/json"" ...

  8. Go 每日一库之 gorilla/mux

    简介 gorilla/mux是 gorilla Web 开发工具包中的路由管理库.gorilla Web 开发包是 Go 语言中辅助开发 Web 服务器的工具包.它包括 Web 服务器开发的各个方面, ...

  9. 路由复用器--gorilla/mux

    简介 gorilla/mux是 gorilla Web 开发工具包中的路由管理库.gorilla Web 开发包是 Go 语言中辅助开发 Web 服务器的工具包.它包括 Web 服务器开发的各个方面, ...

最新文章

  1. RPC 笔记(05)— socket 通信(单线程服务器)
  2. Android 多媒体------相机
  3. 【正一专栏】曼城攻击力惊人露出冠军相
  4. 生产者消费者模式 php 【转】
  5. java面向对象第六章
  6. 受困于敏捷开发的数据与架构?肿么办?
  7. Mac使用技巧:在“快速查看”中查看和编辑文件
  8. 都昌时间轴控件功能说明
  9. html追加消除,HTML/CSS:在中间清除浮动元素而不添加不需要的标签
  10. Python-URL编码和URL解码方法
  11. 解决Error response from daemon: conflict: unable to delete bf6a13bd36ca (must be forced)
  12. python 条件循环赋值_python学习(五) 条件、循环和其他语句
  13. 手机wps取消不等宽分栏_wps取消分栏怎么设置
  14. Python 常用写法
  15. 使用Dreamweaver创建一个PHP程序
  16. Filament 渲染引擎剖析 之 创建渲染对象 1
  17. Word中的滚动文本框是如何制作(可以)
  18. 网络基础-IP地址详解
  19. java毕业设计房屋租赁系统mybatis+源码+调试部署+系统+数据库+lw
  20. Selenium 详细教程

热门文章

  1. Vrpn源码浅析(三)-添加optitrack追踪设备
  2. python机器学习学习通第一章答案详解
  3. pdf合到一起java_将多个PDF文件合并/转换为一个PDF
  4. 海康威视主码流和子码流的区别
  5. js验证固定电话、手机号码
  6. AI医学诊断基础-CT扫描、核磁共振成像(MRI)、拍X光、拍胸片、做B超/彩超等常规检查的介绍、原理、医学影像示例(持续跟新和答疑。。。)
  7. 计算工资、劳务税前或税后收入的JS代码(ES6)
  8. 播放器地址抓取 php,PHP实现使用优酷土豆视频地址获取swf播放器分享地址
  9. Linux常用命令(二)
  10. 什么样的资金盘能活一年,还上了热搜?