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

参考资料:
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. R语言使用table函数计算单分类变量的频率表(frequency table)、使用prop.table函数将table函数计算获得的频率表转化为比率表、返回单分类变量每一个类别的比率、或者百分比
  2. backup ram不稳定 stm32_STM32学习笔记
  3. centos环境变量设置
  4. 实现跨浏览器html5表单验证
  5. form表单提交时,同一个名字的input类型的两个同时提交会覆盖吗
  6. LocalDateTime - Java处理日期和时间
  7. json C库源码地址
  8. java验证jdk_jdk下载,配置,验证
  9. 读者教育浏览器兼容解决方法
  10. GNU大型项目构建和覆盖率生成(第一篇)
  11. 基于51/52单片机毕业设计课题选题表/毕设题目/设计资料
  12. O2O(online to offline)营销模式
  13. reStructuredText 表格快速生成
  14. python做题记录之切西瓜
  15. 计算机视觉、模式识别、机器学习常用牛人主页链接
  16. Transphorm第三代经JEDEC认证的GaN半导体将助力稳态光电全新的1.6 kW钛金级ATX PC游戏电源
  17. 在家如何用手机赚钱,小编来为你一一解答!
  18. 使Celery 4在Windows上运行的2种方法
  19. 如何将dxf或dwg等CAD文件与卫星影像地图叠加进行绘图设计?
  20. R语言绘图patchwork拼图详解快速实现组合图拼接

热门文章

  1. 虚拟服务器的克隆,怎么克隆远程服务器上的虚拟机
  2. 山东超级计算机神威,世界最快超级计算机“神威·太湖之光”获得100多项应用成果...
  3. Blender:导入obj渲染及导出图片+深度图+法向图
  4. 计算机怎么改磁盘位置,如何修改磁盘0和磁盘1的硬盘位置
  5. STM32实现自定义HID复合设备
  6. 2021年华为总监知乎1867赞的Java面试题全集解析助我修行,不吃透感觉都对不起他(上)
  7. 构建ROP链实现远程栈溢出
  8. 解决页面favicon.ico文件不存在提示404问题
  9. 【论文分享】Relation-Aware Graph Attention Network for Visual Question Answering
  10. 阿里巴巴大数据学院落地成都,计划5年培养2000名高端专业人才