go语言negroni包介绍

前言

go语言很好地支持了网络编程,go 语言与web 框架相关的包有很多,本文主要介绍go语言的negroni包。

前置参考博客:HTTP 协议 与 golang web 应用服务
这篇文章中潘老师介绍了go web的基本使用。

下面我们来了解一下negroni包。

negroni包是什么

negroni包是go的一个第三方库,是为了方便使用 net/http 而设计的一个库,由于该包设计优雅,易于扩展,被广泛使用。

negroni官方地址:https://github.com/urfave/negroni

negroni官方中文文档:https://github.com/urfave/negroni/blob/master/translations/README_zh_CN.md

negroni包的使用见官方文档,官方文档的教程很详细,可读性也还可以。

negroni包源码分析

negroni包源码在github仓库中,可以从github上克隆下来或者通过go get命令获得。

negroni包的源码结构如下(使用tree命令获得):

.
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc.go
├── go.mod
├── logger.go
├── logger_test.go
├── negroni.go
├── negroni_bench_test.go
├── negroni_test.go
├── recovery.go
├── recovery_test.go
├── response_writer.go
├── response_writer_pusher.go
├── response_writer_pusher_test
├── response_writer_test.go
├── static.go
├── static_test.go
└── translations├── README_de_de.md├── README_fr_FR.md├── README_ja_JP.md├── README_ko_KR.md├── README_pt_br.md├── README_zh_CN.md└── README_zh_tw.md1 directory, 25 files

源码中真正起作用的只有logger.go、negroni.go、recovery.go、response_writer.go、response_writer_pusher.go、static.go共六个文件,其余都是文档和测试代码。
这六个文件的大小加起来只有18.8k,由此可见negroni包的小而精。

本文只对negroni.go这个文件进行分析,有兴趣的话可以自己看源码。
negroni.go这个文件只有不到两百行,定义了negroni包的接口。

我们来逐行分析negroni.go的程序。

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

首先是对Handler接口的定义,Handler接口要求实现ServeHTTP函数,参数类型是回复的写出流http.ResponseWriter、请求http.Request、回调函数http.HandlerFunc,
然后是对HandlerFunc的函数类型的定义,HandlerFunc的函数参数和Handler.ServeHTTP一致,都是回复、请求和回调函数三个参数。HandlerFunc是一个适配器,用于将外界的函数转化为能够被Negroni处理的类型。
然后定义HandlerFunc实现了ServeHTTP的虚函数,所以HandlerFunc可以转为Handler类型。

type middleware struct {handler Handler// nextfn stores the next.ServeHTTP to reduce memory allocatenextfn func(rw http.ResponseWriter, r *http.Request)
}func newMiddleware(handler Handler, next *middleware) middleware {return middleware{handler: handler,nextfn:  next.ServeHTTP,}
}func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {m.handler.ServeHTTP(rw, r, m.nextfn)
}

接下来是中间件middleware的类型定义,可见中间件类型包括了一个Handler和一个回调函数,middleware包含回调函数的目的是减少内存的分配。
middleware实现了net/http里定义的ServeHTTP接口,通过调用middleware的handler的ServeHTTP来实现,这里形成了Negroni与原生的net/http包的无缝衔接。


// 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)})
}// WrapFunc converts a http.HandlerFunc 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 WrapFunc(handlerFunc http.HandlerFunc) Handler {return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {handlerFunc(rw, r)next(rw, r)})
}

接下来是定义装饰器,Wrap将http.Handler转换为一个negroni.Handler,WrapFunc将http.HandlerFunc转换为一个negroni.Handler,这里也是为了实现Negroni与原生的net/http包的无缝衔接。

// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
// Negroni middleware is evaluated in the order that they are added to the stack using
// the Use and UseHandler methods.
type Negroni struct {middleware middlewarehandlers   []Handler
}// New returns a new Negroni instance with no middleware preconfigured.
func New(handlers ...Handler) *Negroni {return &Negroni{handlers:   handlers,middleware: build(handlers),}
}// With returns a new Negroni instance that is a combination of the negroni
// receiver's handlers and the provided handlers.
func (n *Negroni) With(handlers ...Handler) *Negroni {currentHandlers := make([]Handler, len(n.handlers))copy(currentHandlers, n.handlers)return New(append(currentHandlers, handlers...)...,)
}// Classic returns a new Negroni instance with the default middleware already
// in the stack.
//
// Recovery - Panic Recovery Middleware
// Logger - Request/Response Logging
// Static - Static File Serving
func Classic() *Negroni {return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}

再接下来是Negroni结构的定义以及构造函数,Negroni定义为一个middleware和一个handlers数组组成的结构体
Negroni的构造方法有三个:

  1. New,通过给出一系列的handler来构造Negroni,不定参数handlers组成Negroni的handlers数组,middleware通过辅助函数build来通过这一系列handler构造
  2. With,With通过一个已有的Negroni,并给出不定参数handlers,将给出的handlers附加到已有的Negroni的handlers后即可得到新的Negroni。
  3. Classic, Classic通过在栈中的默认的中间件构造,使用了Recovery、Logger、Static来给出保存的信息。
func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}// 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)
}// UseFunc adds a Negroni-style handler function onto the middleware stack.
func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {n.Use(HandlerFunc(handlerFunc))
}// 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))
}// UseHandlerFunc adds a http.HandlerFunc-style handler function onto the middleware stack.
func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {n.UseHandler(http.HandlerFunc(handlerFunc))
}// 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实现的接口和定义的成员函数。

余下部分是辅助函数。

结语

Negroni设计巧妙,用了适配器等设计模式,思想内涵丰富,需要学习的话可以自己阅读源码来深入研究。

go语言negroni包介绍相关推荐

  1. r语言degseq2_R语言DESeq 包介绍 -

    R语言DESeq包介绍 分析RNA序列数据的一个主要任务是探测基因的差异表达,DESeq包提供了测试差异表达的方法,应用负二项分布和收缩的分布方程估计. 1. 包的安装 输入如下命令,DESeq和相关 ...

  2. R语言lattice包介绍

    lattice包是一个非常强大地高级绘图程序包,由Deepayan Sarkar编写,这个程序包使20世纪90年代初期在贝尔实验室发展起来的特雷里斯图形框架(Trellis)变成了现实. lattic ...

  3. go语言 第三方包安装方法_【分享吧】Go语言第三方包的使用介绍与场景应用

    Go语言是一种跨平台(Mac OS.Windows.Linux 等)的静态编译型语言.拥有媲美C语言的强大性能,支持静态类型安全,在普通计算机上能几秒内快速编译一个大项目,开发效率跟动态语言相差无几. ...

  4. Go 语言的包依赖管理

    对于从 Ruby.Python 或者 Node 等编程语言转向 Go 语言的开发者,可能会有一个疑问: Go 语言中的包依赖关系是怎么管理的?有没有什么方便使用的工具呢? 我最近研究了一下这个问题,以 ...

  5. go语言复数包_go语言学习之包和变量详解

    前言 本文主要介绍了关于go语言之包和变量的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一.包的概念 包是go语言中不可缺少部分,在每个go源码的第一行进行定义,定义方 ...

  6. go语言csv包_玩转数据处理120题R语言版本

    点击上方"早起Python",关注并星标公众号 和我一起玩Python 本文为玩转数据处理120题|R语言版本 习题|刘早起,解答|陈熹 大家好,本文为R语言数据处理120题系列完 ...

  7. Spring目录结构和基础JAR包介绍

    目前 Spring 框架的最新版本是 5.1.8,本教程是基于 Spring 的稳定版本 3.2.13 进行讲解的.读者可以通过网址 http://repo.spring.io/simple/libs ...

  8. 数据分析必备:掌握这个R语言基础包1%的功能,你就很牛了

    导读:无论数据分析的目的是什么,将数据导入R中的过程都是不可或缺的.毕竟巧妇难为无米之炊. utils包是R语言的基础包之一.这个包最重要的任务其实并不是进行数据导入,而是为编程和开发R包提供非常实用 ...

  9. c语言 r语言 java,R语言rJava包安装载入及JAVA环境配置

    rJava 包的安装与载入 一般文本分词的教程都会贴出: install.packages("rJava") library(rJava) 来引导我们装载rJava包,运行inst ...

最新文章

  1. python wasm_Python-pywasm-美味尝鲜
  2. MySQL高级 - 锁 - InnoDB行锁 - 类型
  3. TOMCAT websocket 多连接内存泄漏与jetty对比分析
  4. spring定时注解方式定时写到xml里面融合
  5. mysql 建表时建立索引_mysql 分享建表和索引的几点规范
  6. 转:Page.ClientScript.RegisterStartupScript(me.GetType(),script1,scriptalert('111');/script)...
  7. linux中shell curl命令获取http状态码--------强大的网络传输工具
  8. rk3399_android7.1的HDMI显示实现固定分辨率
  9. 第7章 特种文献检索
  10. STM32——HAL库函数版——AD7656驱动程序
  11. 毕设题目:Matlab肌电信号
  12. Eclipse在桌面上创建的快捷方式打不开
  13. 大数据仓库之拉链表讲解与举例说明【基础部分】
  14. 复合梯形法求积分c语言,复合梯形公式求积分
  15. c++::举例_举例说明:网络
  16. 相机镜头光学中的一些疑难问题的解释
  17. hooks-riverpod 使用
  18. 有没有好人看看,谢谢谢谢
  19. wps里表格中间的字怎么置顶_怎么把wps文字放在最中间
  20. 【YOLOv5 数据集划分】训练和验证、训练验证和测试(train、val)(train、val、test)

热门文章

  1. HTML5网页好看的一些特效
  2. 专升本 计算机 公共课学习笔记(持续更新中...)
  3. 【论文笔记】PassGAN: A Deep Learning Approach for Password Guessing
  4. Floyd算法、Dijkstra算法例题
  5. c++ 箭头符号怎么打_C++编程基础知识二
  6. Python爬虫学习笔记:概念、知识和简单应用
  7. 微信小程序界面设计入门课程-样式wxss中使用css课程-字体-font-style字体风格
  8. Python人脸识别项目-人脸检测
  9. 移动双臂机器人仿真[0]--概述
  10. unity新粒子系统的碰撞和触发