原创文章转载请注明出处
前言
Martini框架是使用Go语言作为开发语言的一个强力的快速构建模块化web应用与服务的开发框架。
阅读本文之前可以先去看看《go利用(*interface{})(nil)传递参数类型》和《Invoke如何动态传参》,这两篇文章介绍了
Martini
深度依赖的inject
包的一些实现细节。
Martini
的官方文档中提到Martini
完全兼容http.HandlerFunc接口.,底下谈到martini.Context
的初始化就会有说明。
先来看看Martini的结构体。
// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.
type Martini struct {
inject.Injector
handlers []Handler
action Handler
logger *log.Logger
}
inject.Injector
是inject
接口实例,Martini
高度依赖inject
。
handlers
是切片存储Hander
类型,Handler
是自定义类型。
type Handler interface{}
Martini
有两种处理器,中间件处理器和请求处理器。中间件处理器通过Use
方法将中间件追加保存到handlers
切片中,请求处理器需要搭配路由进行存储。
初始化
为了更快速的启用Martini
, martini.Classic() 提供了一些默认的方便Web开发的工具:
m := martini.Classic()
// ... middleware and routing goes here
m.Run()
下面是Martini核心已经包含的功能 martini.Classic():
- Request/Response Logging (请求/响应日志) - martini.Logger
- Panic Recovery (容错) - martini.Recovery
- Static File serving (静态文件服务) - martini.Static
- Routing (路由) - martini.Router
下面的这些服务已经被包含在核心Martini中: martini.Classic():
- *log.Logger - Martini的全局日志.
- martini.Context - http request context (请求上下文).
-
martini.Params -
map[string]string
of named params found by route matching. (名字和参数键值对的参数列表) - martini.Routes - Route helper service. (路由协助处理)
- http.ResponseWriter - http Response writer interface. (响应结果的流接口)
- *http.Request - http Request. (http请求)
martini.Context是每次请求的上下文。
// Context represents a request context. Services can be mapped on the request level from this interface.
type Context interface {
inject.Injector
// Next is an optional function that Middleware Handlers can call to yield the until after
// the other Handlers have been executed. This works really well for any operations that must
// happen after an http request
Next()
// Written returns whether or not the response for this context has been written.
Written() bool
}
它是什么时候被创建的呢?还记得《理解go的function types》吗?
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)
martini.go
实现了ServerHTTP方法。
// ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server.
func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
m.createContext(res, req).run()
}
因为Martini
实现了http.HandlerFunc接口,所以它可以很简单的应用到现有Go服务器的子集中。
package hello
import (
"net/http"
"github.com/go-martini/martini"
)
func init() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
http.Handle("/", m)
}
martini.Context的实例m.context
就是在go服务器初始化的时候通过ServeHTTP
被创建的。
我们已经知道Martini如何应用到Go服务器的子集,那么当服务器运行的时候,处理器是如何被执行的呢?
运行
我们看看context
结构体
type context struct {
inject.Injector
handlers []Handler
action Handler
rw ResponseWriter
index int
}
func (c *context) handler() Handler {
if c.index < len(c.handlers) {
return c.handlers[c.index]
}
if c.index == len(c.handlers) {
return c.action
}
panic("invalid index for context handler")
}
func (c *context) Next() {
c.index += 1
c.run()
}
func (c *context) Written() bool {
return c.rw.Written()
}
func (c *context) run() {
for c.index <= len(c.handlers) {
_, err := c.Invoke(c.handler())
if err != nil {
panic(err)
}
c.index += 1
if c.Written() {
return
}
}
}
context
实现了Context
接口,自然也组合了inject.Injector
。处理器处理请求的时候通过run()
循环遍历调用handlers
中的所有中间件和路由处理方法。
看到这里是不是发现一个很眼熟的函数Invoke()
,没错又是inject
的Invoke()
《Invoke如何动态传参》,我之前的文章分析过inject
是Martini
的核心。
我们可以总结一下处理器的运行流程,初始化的时候使用inject
接口绑定数据,在上下文中保存处理器函数。服务器运行以后,每次处理请求就在上下文中执行处理器函数,并且利用依赖注入动态传参完成请求处理。
后记
本文没有涉及到Martini
的路由设计和中间件的使用技巧,改日再专门写一篇文章进行介绍。
我是咕咕鸡,一个还在不停学习的全栈工程师。
热爱生活,喜欢跑步,家庭是我不断向前进步的动力。