golang源码分析(37)martini

2022-08-02 17:12:18 浏览数 (1)

Martini的官方文档中提到Martini完全兼容http.HandlerFunc接口.,底下谈到martini.Context的初始化就会有说明。

先来看看Martini的结构体。

代码语言:javascript复制
// 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.Injectorinject接口实例,Martini高度依赖injecthandlers是切片存储Hander类型,Handler是自定义类型。

代码语言:javascript复制
type Handler interface{}

Martini有两种处理器,中间件处理器和请求处理器。中间件处理器通过Use方法将中间件追加保存到handlers切片中,请求处理器需要搭配路由进行存储。

初始化

为了更快速的启用Martini, martini.Classic() 提供了一些默认的方便Web开发的工具:

代码语言:javascript复制
  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是每次请求的上下文。

代码语言:javascript复制
// 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》吗?

代码语言:javascript复制
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)

martini.go实现了ServerHTTP方法。

代码语言:javascript复制
// 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服务器的子集中。

代码语言:javascript复制
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结构体

代码语言:javascript复制
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(),没错又是injectInvoke()

0 人点赞