概述

我们可以先看一下HTTP Server的处理逻辑:

在这个逻辑处理的环节中,Negroni充当一个HTTP Handler的角色,并对于所有的HTTP Request的处理都会通过Negroni被转交到其内部的子中间件

实现Negroni

Negroni本质上可以被看作一个中间件是因为它实现了HTTP Handler接口,因此它可以被http.ListenAndServe()调用:

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}

而Negroni本身内部也具有自身的中间件,也就是标准库http调用中间件Negroni,然后Negroni调用自身的中间件来完成对各种HTTP Request的处理

这样设计的好处在于:对于HTTP Request来说,相当与有了一个统一的接口,所有的对HTTP Request的处理都会被转交到Negroni内部的子中间件进行处理,这样子Negroni相当与一个集线器的作用

子中间件的接口为:

type Handler interface {ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

Negroni自身的Handler定义与标准库http的定义非常相似,只是多了一个参数next,这个参数使得Negroni的中间件形成链状结构

Negroni中间件链

上述我们提到Negroni对于HTTP Handler的实现的区别只在于多了一个参数next,这是因为Negroni内部会维护一条Handler链,源码如下:

type Negroni struct {middleware middlewarehandlers   []Handler
}func New(handlers ...Handler) *Negroni {return &Negroni{handlers:   handlers,middleware: build(handlers),}
}func build(handlers []Handler) middleware {var next middlewareif len(handlers) == 0 {return voidMiddleware()} else if len(handlers) > 1 {next = build(handlers[1:])} else {next = voidMiddleware()}return middleware{handlers[0], &next}
}func Classic() *Negroni {return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}

其中build函数会递归创建一个类似链表结构的Handler链,结构如下:

初始化Negroni

我们可以调用Negroni.Classic()函数来创建一个Negroni指针:

func Classic() *Negroni {return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}

然后Classic()函数会调用New函数来创建并初始化一个Negroni对象并将指针返回:

func New(handlers ...Handler) *Negroni {return &Negroni{handlers:   handlers,middleware: build(handlers),}
}

New函数中,会将传入的handlers保存下来并传给build方法,然后build方法会按照上面所说的方式来构建一条Handler链

Negroni.UseHandler实现

我们可以用Negroni.UseHandler来实现我们自定义的Handler,源码如下:

func (n *Negroni) UseHandler(handler http.Handler) {n.Use(Wrap(handler))
}

方式是将传入的Handler进行一定的包装然后交给Use函数处理:

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)
}

然后Use函数再将包装过的Handler加在Handler链的末尾和放到一个数组中保存

其中包装函数Wrap的实现为:

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)})
}

因此UseHandler函数本质上是将http.Handler转换为negroni.handler的过程

Negroni.Run实现

使用Negroni.Run函数可以运行我们的服务器,其源码为:

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本身也是一个Handler,因此可以直接被http.ListenAndServe调用:

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}

http.ListenAndServe调用Negroni.ServeHTTP接口方法时,其实是调用中间件的Handler实现的ServeHTTP接口方法:

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

因此Negroni的作用相当与一个集线器或者说Controller,将多个Handler统筹起来,让它们对于http.ListenAndServe有一个统一的接口

Negroni中间件源码分析相关推荐

  1. golang学习之negroni/gizp源码分析

    在 Go 语言里,Negroni 是一个很地道的 Web 中间件,它是一个具备微型.非嵌入式.鼓励使用原生 net/http 库特征的中间件.利用它地Use功能,我们可以很简单地自定义中间件并使用.其 ...

  2. 初识洋葱模型,分析中间件执行过程,浅析koa中间件源码

    前言 作为洋葱模型的第一篇文章,这里仅介绍了一些入门级知识,比如 了解洋葱模型执行顺序 分析部分 koa 中间件的源码来加深对中间件的认识 为第二篇文章:分析洋葱模型实现原理,在自己项目中接入洋葱模型 ...

  3. Koa2和Redux中间件源码研究

    一.Koa2中间件源码分析 在Koa2中,中间件被存放在一个数组中. 使用koa中,最常见的就是app.use(fn),use函数部分源码如下所示.首先中间件必须是个函数.若是generator函数, ...

  4. Django源码分析5:session会话中间件分析

    django源码分析 本文环境python3.5.2,django1.10.x系列 1.这次分析django框架中的会话中间件. 2.会话保持是目前框架都支持的一个功能,因为http是无状态协议,无法 ...

  5. Django源码分析4:staticfiles静态文件处理中间件分析

    django源码分析 本文环境python3.5.2,django1.10.x系列1.在上一篇文章中已经分析过handler的处理过程,其中load_middleware就是将配置的中间件进行初始化, ...

  6. 数据库中间件 MyCAT源码分析 —— XA分布式事务

    title: MyCAT 源码分析 -- XA分布式事务 date: 2017-07-15 tags: categories: MyCAT permalink: MyCAT/xa-distribute ...

  7. koa2 一网打尽(基本使用,洋葱圈,中间件机制和模拟,源码分析,核心点,生态)

    原文https://juejin.im/entry/5bfbe5a76fb9a049cb186cfa/detail koa homepage 优秀的下一代 web 开发框架. Koa 应用程序不是 H ...

  8. negroni-gzip源码分析

    gibhub地址:针对negroni的gzip 作业内容:支持了gzip的服务器小程序 这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip.源码共有一百多 ...

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

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

最新文章

  1. Ubuntu 系统安装Visual Studio Code
  2. 只需一行代码,你的纯文本秒变 Markdown
  3. sm4 前后端 加密_这7个开源的Spring Boot前后端分离项目整理给你
  4. 记一次discuz修改首页图片路径问题
  5. 查看mysql半杯_如何通过show slave status的输出使用change master to命令 | 半瓶
  6. 计算机语言语法语义,程序设计语言语义
  7. php mysql 进销存_PHP进销存源码 仓库管理系统 WEB进销存 php+mysql 网络版进销存
  8. c语言case用多重语句,switch多重选择
  9. Eclipse中Folder和SourceFolder
  10. python绘制直线的垂线_Matplotlib:散点图中的垂直线
  11. Mobicomm 2019
  12. AutoFac在WinForm中的使用
  13. td自动换行时不切断英文单词
  14. QUIC构建1——chromium镜像,clone文件depot_tools的过程+git过程中的问题
  15. 苹果pencil值得买吗?苹果平板电容笔推荐
  16. C99:C标准库接口的头文件集和功能定义参考
  17. 企业 Apple 设备管理概述
  18. matlab 代码分析 内存溢出,matlab内存溢出的解决方案
  19. vs2008 的一些编译常识
  20. 计算机图形学教程答案,《计算机图形学教程》试卷A答案

热门文章

  1. 03 关键测量指标——整体网络测量
  2. 4 行代码写 3 个NPE异常,服了!
  3. springboot+Vue开发的 ktv预定管理系统
  4. vue -V 执行失败 檔案名稱、目錄名稱或磁碟區標籤語法錯誤。
  5. python应用——分治法实现循环赛
  6. 微信运动服务器多久同步一次,微信运动多久更新一次步数(微信运动刷新时间表)...
  7. 图片的居中定位和按钮定位
  8. cdr存成html格式的文件格式,CDR必备!CDR保存与各种格式之间转换!-cdr文件用什么打开...
  9. 复利计算计算器_C程序计算复利
  10. 语音与影像上的自督导式学习模型、一些老版本的补充(李宏毅2022