原創(chuàng)文章轉(zhuǎn)載請(qǐng)注明出處
前言
Martini框架是使用Go語(yǔ)言作為開(kāi)發(fā)語(yǔ)言的一個(gè)強(qiáng)力的快速構(gòu)建模塊化web應(yīng)用與服務(wù)的開(kāi)發(fā)框架。
閱讀本文之前可以先去看看《go利用(*interface{})(nil)傳遞參數(shù)類(lèi)型》和《Invoke如何動(dòng)態(tài)傳參》雹食,這兩篇文章介紹了
Martini
深度依賴(lài)的inject
包的一些實(shí)現(xiàn)細(xì)節(jié)。
Martini
的官方文檔中提到Martini
完全兼容http.HandlerFunc接口.,底下談到martini.Context
的初始化就會(huì)有說(shuō)明。
先來(lái)看看Martini的結(jié)構(gòu)體粘室。
// 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
接口實(shí)例马昙,Martini
高度依賴(lài)inject
桃犬。
handlers
是切片存儲(chǔ)Hander
類(lèi)型,Handler
是自定義類(lèi)型行楞。
type Handler interface{}
Martini
有兩種處理器攒暇,中間件處理器和請(qǐng)求處理器。中間件處理器通過(guò)Use
方法將中間件追加保存到handlers
切片中子房,請(qǐng)求處理器需要搭配路由進(jìn)行存儲(chǔ)形用。
初始化
為了更快速的啟用Martini
, martini.Classic() 提供了一些默認(rèn)的方便Web開(kāi)發(fā)的工具:
m := martini.Classic()
// ... middleware and routing goes here
m.Run()
下面是Martini核心已經(jīng)包含的功能 martini.Classic():
- Request/Response Logging (請(qǐng)求/響應(yīng)日志) - martini.Logger
- Panic Recovery (容錯(cuò)) - martini.Recovery
- Static File serving (靜態(tài)文件服務(wù)) - martini.Static
- Routing (路由) - martini.Router
下面的這些服務(wù)已經(jīng)被包含在核心Martini中: martini.Classic():
- *log.Logger - Martini的全局日志.
- martini.Context - http request context (請(qǐng)求上下文).
-
martini.Params -
map[string]string
of named params found by route matching. (名字和參數(shù)鍵值對(duì)的參數(shù)列表) - martini.Routes - Route helper service. (路由協(xié)助處理)
- http.ResponseWriter - http Response writer interface. (響應(yīng)結(jié)果的流接口)
- *http.Request - http Request. (http請(qǐng)求)
martini.Context是每次請(qǐng)求的上下文就轧。
// 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
}
它是什么時(shí)候被創(chuàng)建的呢?還記得《理解go的function types》嗎尾序?
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)
martini.go
實(shí)現(xiàn)了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()
}
因?yàn)?code>Martini實(shí)現(xiàn)了http.HandlerFunc接口,所以它可以很簡(jiǎn)單的應(yīng)用到現(xiàn)有Go服務(wù)器的子集中每币。
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的實(shí)例m.context
就是在go服務(wù)器初始化的時(shí)候通過(guò)ServeHTTP
被創(chuàng)建的携丁。
我們已經(jīng)知道Martini如何應(yīng)用到Go服務(wù)器的子集,那么當(dāng)服務(wù)器運(yùn)行的時(shí)候兰怠,處理器是如何被執(zhí)行的呢梦鉴?
運(yùn)行
我們看看context
結(jié)構(gòu)體
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
實(shí)現(xiàn)了Context
接口,自然也組合了inject.Injector
揭保。處理器處理請(qǐng)求的時(shí)候通過(guò)run()
循環(huán)遍歷調(diào)用handlers
中的所有中間件和路由處理方法肥橙。
看到這里是不是發(fā)現(xiàn)一個(gè)很眼熟的函數(shù)Invoke()
,沒(méi)錯(cuò)又是inject
的Invoke()
《Invoke如何動(dòng)態(tài)傳參》秸侣,我之前的文章分析過(guò)inject
是Martini
的核心存筏。
我們可以總結(jié)一下處理器的運(yùn)行流程,初始化的時(shí)候使用inject
接口綁定數(shù)據(jù)味榛,在上下文中保存處理器函數(shù)椭坚。服務(wù)器運(yùn)行以后,每次處理請(qǐng)求就在上下文中執(zhí)行處理器函數(shù)搏色,并且利用依賴(lài)注入動(dòng)態(tài)傳參完成請(qǐng)求處理善茎。
后記
本文沒(méi)有涉及到Martini
的路由設(shè)計(jì)和中間件的使用技巧,改日再專(zhuān)門(mén)寫(xiě)一篇文章進(jìn)行介紹频轿。
我是咕咕雞垂涯,一個(gè)還在不停學(xué)習(xí)的全棧工程師。
熱愛(ài)生活航邢,喜歡跑步耕赘,家庭是我不斷向前進(jìn)步的動(dòng)力。