go语言很好地支持了网络编程,go 语言与web 框架相关的包有很多,本文主要介绍go语言的negroni包。
前置参考博客:HTTP 协议 与 golang web 应用服务
这篇文章中潘老师介绍了go web的基本使用。
下面我们来了解一下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包源码在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.md
1 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 allocate
nextfn 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 middleware
handlers []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的构造方法有三个:
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设计巧妙,用了适配器等设计模式,思想内涵丰富,需要学习的话可以自己阅读源码来深入研究。