我们按照git主页的一些示例深入源码解读。在阅读此文之前,你需要了解Inject的原理,可以参阅golang: Martini之inject源码分析
首先来看一个最基本的实例:
package main
import "github.com/go-martini/martini"
func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
m.Run()
}
此处使用了3个方法:
我们依次解释
首先介绍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 //处理handler,一般赋值路由处理方法
logger *log.Logger //日志处理对象
}
在martini.Classic()中,我们新建了一个经典的martini对象,用来支持常规的应用场景。当然你可以根据自己的需求定制。
// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static.
// Classic also maps martini.Routes as a service.
func Classic() *ClassicMartini {
r := NewRouter() 新建标准路由
m := New() 新建Martini对象
m.Use(Logger())//注册中间件,标准logger
m.Use(Recovery()) //注册中间件,标准recover
m.Use(Static("public")) //注册中间件Static
m.MapTo(r, (*Routes)(nil)) //Injector的Mapto方法,实现类型和对象的关联注入
m.Action(r.Handle) //设置处理函数为路由处理函数
return &ClassicMartini{m, r} //返回一个ClassMartini实例,继承了Martini的结构, 提升了martini以及Router的相关方法
}
其中NewRouter新建一个路由空对象:
// NewRouter 创建一个路由实例
// 如果你不使用 ClassicMartini, 你需要手动注册路由(Classic会自动为你做这个):
//
// m := martini.New()
// r := martini.NewRouter()
// m.MapTo(r, (*martini.Routes)(nil))
//
func NewRouter() Router {
return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}
martini.New()会新建一个Martini实例,注册一个写到标准输出的logger,注册一个defaultReturnHandler()
func New() *Martini {
m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}
m.Map(m.logger) // log.Logger对应 m.logger
m.Map(defaultReturnHandler()) //ReturnHandler对应defaultReturnHandler()的返回函数
return m
}
这个defaultRetrunHandler()是什么鬼。。。。。。。
type ReturnHandler func(Context, []reflect.Value)
func defaultReturnHandler() ReturnHandler {
return func(ctx Context, vals []reflect.Value) { //vals应该就是返回值了
rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) //从ctx中取出http.ResponseWriter类型的对象
res := rv.Interface().(http.ResponseWriter)//从reflect.Value转化为http.ResponseWriter
var responseVal reflect.Value
if len(vals) > 1 && vals[0].Kind() == reflect.Int { //返回值第一个如果是int就将其写到返回的http头当中
res.WriteHeader(int(vals[0].Int()))
responseVal = vals[1]
} else if len(vals) > 0 {
responseVal = vals[0]
} //接下来的vals写到responseVal
if canDeref(responseVal) {//如果返回值是接口活指针则解引用到其包含或者指向对象
responseVal = responseVal.Elem()
}
if isByteSlice(responseVal) {输出结果到http返回的body
res.Write(responseVal.Bytes())
} else {
res.Write([]byte(responseVal.String()))
}
}
}
这里的defaultReturnHandler就解释了实例中如下的结果,可以直接把状态码由第一个参数返回
m.Get("/", func() (int, string) {
return 418, "i'm a teapot" // HTTP 418 : "i'm a teapot"
})
注册一个中间件, 将一个可处理的handler添加到Martini.handlers的数组之后:
func (m *Martini) Use(handler Handler) {
validateHandler(handler)
m.handlers = append(m.handlers, handler)
}
实际是Injector.Mapto的方法提升,注册路由
简单的注册路由处理函数为该Martini处理实例的处理函数
func (m *Martini) Action(handler Handler) {
validateHandler(handler)
m.action = handler
}
实际上是Router.Get()
首先我们先来看一下Router接口:
type Router interface {
Routes
// Group adds a group where related routes can be added.
Group(string, func(Router), ...Handler)
// Get adds a route for a HTTP GET request to the specified matching pattern.
Get(string, ...Handler) Route
// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
Patch(string, ...Handler) Route
// Post adds a route for a HTTP POST request to the specified matching pattern.
Post(string, ...Handler) Route
// Put adds a route for a HTTP PUT request to the specified matching pattern.
Put(string, ...Handler) Route
// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
Delete(string, ...Handler) Route
// Options adds a route for a HTTP OPTIONS request to the specified matching pattern.
Options(string, ...Handler) Route
// Head adds a route for a HTTP HEAD request to the specified matching pattern.
Head(string, ...Handler) Route
// Any adds a route for any HTTP method request to the specified matching pattern.
Any(string, ...Handler) Route
// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
AddRoute(string, string, ...Handler) Route
// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
NotFound(...Handler)
// Handle is the entry point for routing. This is used as a martini.Handler
Handle(http.ResponseWriter, *http.Request, Context)
}
type router struct {
routes []*route
notFounds []Handler
groups []group
routesLock sync.RWMutex
}
其中的router接口包含了很多http方法的注入函数,其中的Get() 就是处理Get方法的函数注入:
func (r *router) Get(pattern string, h ...Handler) Route {
return r.addRoute("GET", pattern, h)
}
func (r *router) addRoute(method string, pattern string, handlers []Handler) *route {
if len(r.groups) > 0 {
/**遍历当前路由中的groups,可以看到
* type group struct {
* pattern string
* handlers []Handler
* }
* 每个group都是对应的pattern和一堆的handlers
* 将目前路由中的所有pattern连接起来,作为当前新建的路由规则的pattern
* 同样handlers也需要将之前的所有handler累积起来
* */
groupPattern := ""
h := make([]Handler, 0)
for _, g := range r.groups {
groupPattern += g.pattern
h = append(h, g.handlers...)
}
pattern = groupPattern + pattern
h = append(h, handlers...)
handlers = h
}
route := newRoute(method, pattern, handlers)//根据加入的pattern来新建路由规则
route.Validate()
r.appendRoute(route) //将新生成的路由规则加入路由表
return route
}
可以看到,在上面的addRoute的过程中group中的pattern和handlers会添加到所有的路由规则中,group可以通过显示调用:
func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
r.groups = append(r.groups, group{pattern, h})
fn(r)
r.groups = r.groups[:len(r.groups)-1]
}
在实例代码中,也有两则简易的group使用示例:
m.Group("/books", func(r martini.Router) {
r.Get("/:id", GetBooks)
r.Post("/new", NewBook)
r.Put("/update/:id", UpdateBook)
r.Delete("/delete/:id", DeleteBook)
}, MyMiddleware1, MyMiddleware2)
//依据上面的语义,上面的代码的效果在于
//分别实现了"/books/:id","/books/new", "/books/update/:id", "/books/delete/:id"这几种pattern及其对应的handler,
//同时将[MyMiddleware1, MyMiddleware2]两个handler加到每个route规则的handlers数组当中
首先,一条单独的路由规则如下定义:
type route struct {
method string
regex *regexp.Regexp
handlers []Handler
pattern string
name string
}
在新建一条路由规则的时候:
func newRoute(method string, pattern string, handlers []Handler) *route {
route := route{method, nil, handlers, pattern, ""}
pattern = routeReg1.ReplaceAllStringFunc(pattern, func(m string) string {
return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:])
})
var index int
pattern = routeReg2.ReplaceAllStringFunc(pattern, func(m string) string {
index++
return fmt.Sprintf(`(?P<_%d>[^#?]*)`, index)
})
pattern += `\/?`
route.regex = regexp.MustCompile(pattern)
return &route
}
大概的意思就是将pattern需要进行一些正则处理并编译成自己的正则处理引擎。
在设置好路由和handle之后,run就可以了:
func (m *Martini) RunOnAddr(addr string) {
//从injector中取出对应(*log.Logger)类型的对象,即找出日志对象用来在开始和失败的时候打日志
logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger)
logger.Printf("listening on %s (%s)\n", addr, Env)
logger.Fatalln(http.ListenAndServe(addr, m))
}
func (m *Martini) Run() {
port := os.Getenv("PORT")
if len(port) == 0 {
port = "3000"
}
host := os.Getenv("HOST")
m.RunOnAddr(host + ":" + port)
}
这里需要注意的是
此处的logger和Martini.Classic()中的m.Use(Logger())有所不同,此处取出的logger诞生于martini.New()中的logger: log.New(os.Stdout, “[martini]”, 0),故会打印到标准输出
而Martini.Classic()中的m.Use(Logger())是这样定义的:
func Logger() Handler {
return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) {
start := time.Now()
addr := req.Header.Get("X-Real-IP")
if addr == "" {
addr = req.Header.Get("X-Forwarded-For")
if addr == "" {
addr = req.RemoteAddr
}
}
log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr)
rw := res.(ResponseWriter)
c.Next()//调用context.handlers[]中的下一个handler,这样便实现了中间件的功能
log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start))
}
}
在RunOnAddr()
中的logger.Fatalln(http.ListenAndServe(addr, m))
,要求m实现Handler接口:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
那么martini是何时实现该接口的呢?
如下,在martini.go当中有:
func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
m.createContext(res, req).run()
}
本质是针对http的一个requeset来返回一个responseWriter
新建一个martini.context并运行run()
那么我们现在看一下context的结构:
type Context interface {
inject.Injector
Next()
Written() bool //用来标识是否往responsewriter输出结果
}
type context struct {
inject.Injector
handlers []Handler
action Handler
rw ResponseWriter
index int
}
martini.context实现了以下的方法:
func (c *context) handler() Handler
func (c *context) Next()
func (c *context) Written()
func (c *context) run()
其中最重要的run()
方法:
func (c *context) run() {
for c.index <= len(c.handlers) {
_, err := c.Invoke(c.handler())//依次调用,参数已经由injector注入,先调用handlers,最后调用action
if err != nil {
panic(err)
}
c.index += 1
if c.Written() {
return
}
}
}
依次调用context.handlers中的handler,最后调用action,直到有的返回error引发panic,或者有的往ResponseWriter()输出结果,则结束。
action在martini.Classic()
时被设置为m.Action(r.Handle)
,即router的handle,我们去看一下
func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
bestMatch := NoMatch
var bestVals map[string]string
var bestRoute *route
// 查找最match的路由规则
for _, route := range r.getRoutes() {
match, vals := route.Match(req.Method, req.URL.Path)
if match.BetterThan(bestMatch) {
bestMatch = match
bestVals = vals
bestRoute = route
if match == ExactMatch {
break
}
}
}
//如果找到则执行其handle
if bestMatch != NoMatch {
params := Params(bestVals)
context.Map(params)
bestRoute.Handle(context, res) //其实就是建立一个路由上下文,routeContext,注入context和路由规则,然后run
return
}
// no routes exist, 404
c := &routeContext{context, 0, r.notFounds}
context.MapTo(c, (*Context)(nil))
c.run()// 设置上下文为notfounds方法
}
设置完路由之后的执行run为:
func (r *routeContext) run() {
for r.index < len(r.handlers) {
handler := r.handlers[r.index]
vals, err := r.Invoke(handler)
if err != nil {
panic(err)
}
r.index += 1
// if the handler returned something, write it to the http response
if len(vals) > 0 {
ev := r.Get(reflect.TypeOf(ReturnHandler(nil)))// ReturnHandler这个类型就是刚开始martini.New()中设置的defaultReturnHandler()
//原来在此处调用,哈哈哈哈
handleReturn := ev.Interface().(ReturnHandler)
handleReturn(r, vals)
}
if r.Written() {
return
}
}
}
最后我们来看下createContext()的方法:
//创建一个请求的上下文,可以说一个http请求都是在这个上下文环境中执行的
func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0}//其handlers和action都是继承自martini,NewResponseWriter()只是原先的http.ResponseWriter()增加了一些状态的包装
c.SetParent(m)
c.MapTo(c, (*Context)(nil))
c.MapTo(c.rw, (*http.ResponseWriter)(nil))
c.Map(req)
return c
}
我们依照定义顺序来梳理下整个实例1中,use和map以及get等定义:
Classic():
New():
Map(m.logger)//标准输出的logger
Map(defaultReturnHandler())//简单的返回值和状态码,实际是这样的函数type ReturnHandler func(Context, []reflect.Value),调用c.Next()陷入下一个中间件
Use(Logger())//logger中间件,前后打印日志,需要类型有res http.ResponseWriter, req *http.Request, c Context, log *log.Logger,调用c.Next()陷入下一个中间件
Use(Recovery()) //recovery中间件,从各种panic中恢复回来并设置返回头和body
Use(Static("public")) //静态文件服务,执行完之后不陷入c.Next(),貌似是直接返回的,然后执行下个handle
MapTo(r, (*Routes)(nil))
Action(r.Handle)
Get()://注册路由规则
Run():
ServeHTTP():
c.MapTo(c, (*Context)(nil))
c.MapTo(c.rw, (*http.ResponseWriter)(nil))
c.Map(req)
执行顺序是:
实例1的解释就到这个程度啦,关于路由和中间件的详细内容还没有说,等待后文吧