Martini 框架

Martini核心部分Injector


Injector模块总体构造

injector对象:

  1. type injector struct {
  2. values map[reflect.Type]reflect.Value // 保存<类型,值>对
  3. parent Injector
  4. }

TypeMaper接口

  1. type TypeMapper interface {
  2. Map(interface{}) TypeMapper // 类型映射
  3. MapTo(interface{}, interface{}) TypeMapper //将值映射为指定的类型
  4. Set(reflect.Type, reflect.Value) TypeMapper // 设置类型值
  5. Get(reflect.Type) reflect.Value
  6. }

Injectorde主体是一个Injecotr接口,Injector接口组合了Application,Invoker,以及TypeMaper接口。核心函数Invoker,通过传入一个interface,进行后续的处理。interface的具体类型必须为函数,否则会panic。通过reflect进行反射获取interface的具体函数类型。

  1. func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
  2. t := reflect.TypeOf(f)
  3. var in = make([]reflect.Value, t.NumIn()) //如果传入的interface不是函数,则panic
  4. for i := 0; i < t.NumIn(); i++ {
  5. argType := t.In(i)
  6. val := inj.Get(argType) // 通过函数参数类型获取函数值,injector注册器通过map保存<类型,值>对。
  7. if !val.IsValid() {
  8. return nil, fmt.Errorf("Value not found for type %v", argType)
  9. }
  10. in[i] = val
  11. }
  12. return reflect.ValueOf(f).Call(in), nil
  13. }

injector使用例子:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/codegangsta/inject"
  5. )
  6. type myString interface{}
  7. func test1(st string, myst myString) {
  8. fmt.Println(st, myst)
  9. }
  10. var (
  11. st1 = "hello"
  12. st2 = "WORLD"
  13. myst myString =="haha"
  14. )
  15. func main() {
  16. inj := inject.New() \\生成injector实例
  17. inj.Map(st1) \\ st1注册为默认的类型
  18. inj.MapTo(st2, (*myString)(nil))\\ st2注册为指定的myStrig类型
  19. inj.Invoke(test1) \\ 函数test1通过参数了类型获取参数具体的值
  20.     fmt.Println(inject.InterfaceOf(&myst)) // myst必须为指向interface的指针,否则panic。 }
  21. }
输出:

martini服务模块


Martini结构:

使用Matrini框架时,可以自己直接使用框架内置的classicMartini。使用方法:

  1. package main
  2. import (
  3. "github.com/go-martini/martini"
  4. "fmt"
  5. "reflect"
  6. )
  7. func test1(st string) string {
  8. return st
  9. }
  10. func test2(num int) int {
  11. fmt.Println(num)
  12. return num
  13. }
  14. type INT interface{}
  15. func test3(num INT, st string) string {
  16. st = st + fmt.Sprint(reflect.TypeOf(num))
  17. return st
  18. }
  19. func main() {
  20. m := martini.Classic() //生成martini实例
  21. m.Get("/hello", test2, test1)// 注册路由
  22. m.Get("/hi", test3)
  23. m.Map("a") // 注册函数参数值
  24. m.Map(1)
  25. m.MapTo("HI", (*INT)(nil))
  26. m.Run() //启动监听服务
  27. }

工作流程:


1.使用Classic生成martini实例:

  1. func Classic() *ClassicMartini {
  2. r := NewRouter()
  3. m := New() //生成martini实例
  4. m.Use(Logger()) //注册Handler,实际是往martini.handlers里append一个Handler
  5. m.Use(Recovery())
  6. m.Use(Static("public"))
  7. m.MapTo(r, (*Routes)(nil))
  8. m.Action(r.Handle) // 注册路由处理函数,m.action=r.Handler
  9. return &ClassicMartini{m, r}
  10. }

2.注册路由函数

路由器实现:

Router接口:

  1. type Router interface {
  2. Routes
  3. // Group adds a group where related routes can be added.
  4. Group(string, func(Router), ...Handler)
  5. // Get adds a route for a HTTP GET request to the specified matching pattern.
  6. Get(string, ...Handler) Route
  7. // Patch adds a route for a HTTP PATCH request to the specified matching pattern.
  8. Patch(string, ...Handler) Route
  9. // Post adds a route for a HTTP POST request to the specified matching pattern.
  10. Post(string, ...Handler) Route
  11. // Put adds a route for a HTTP PUT request to the specified matching pattern.
  12. Put(string, ...Handler) Route
  13. // Delete adds a route for a HTTP DELETE request to the specified matching pattern.
  14. Delete(string, ...Handler) Route
  15. // Options adds a route for a HTTP OPTIONS request to the specified matching pattern.
  16. Options(string, ...Handler) Route
  17. // Head adds a route for a HTTP HEAD request to the specified matching pattern.
  18. Head(string, ...Handler) Route
  19. // Any adds a route for any HTTP method request to the specified matching pattern.
  20. Any(string, ...Handler) Route
  21. // AddRoute adds a route for a given HTTP method request to the specified matching pattern.
  22. AddRoute(string, string, ...Handler) Route
  23. // NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
  24. NotFound(...Handler)
  25. // Handle is the entry point for routing. This is used as a martini.Handler
  26. Handle(http.ResponseWriter, *http.Request, Context)
  27. }

例子中使用Get注册路由,Martini中注册路由与其他框架不同的地方在于,路由的函数可以是任意类型,而且同一个路劲可以注册多个处理函数。

注册路由时,实际内部都是使用router.addroute()

  1. func (r *router) addRoute(method string, pattern string, handlers []Handler) *route {
  2. if len(r.groups) > 0 {
  3. groupPattern := ""
  4. h := make([]Handler, 0)
  5. for _, g := range r.groups {
  6. groupPattern += g.pattern
  7. h = append(h, g.handlers...)
  8. }
  9. pattern = groupPattern + pattern
  10. h = append(h, handlers...) //将注册的方法append进handlers ,请求路劲到此路由的时候会遍历调用该handlers,直到函数有返回值
  11. handlers = h
  12. }
  13. fmt.Println("handlers:", reflect.ValueOf(handlers))
  14. route := newRoute(method, pattern, handlers) // 生成指定路径方法的路由
  15. fmt.Println("route:", reflect.ValueOf(route.handlers))
  16. route.Validate()
  17. r.appendRoute(route)
  18. return route
  19. }

addRoute生成一个route并添加进router.routes。当客户端请求时,遍历改routes获取对应路由。

addRoute方法调用了newRoute返回一个*route

route结构

  1. type route struct {
  2. method string
  3. regex *regexp.Regexp
  4. handlers []Handler
  5. pattern string
  6. name string
  7. }

newRoute将注册的方法添加进handlers。

例子中的m.Get("/hello", test2, test1)及将test1,test2这两个函数添加去route.handlers.

当请求路径为/hello时,就会调用这两个方法。

3.map为函数参数注入参数值

4.run启动服务器监听请求

  1. func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
  2. //res.Write([]byte("hello"))
  3. m.createContext(res, req).run()
  4. }

ServerHTTP是每一个request请求实例的入口。

对于每一个req实例,createContext生成一个上下文。

  1. func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
  2. c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0} //生成context实例
  3. c.SetParent(m)
  4. c.MapTo(c, (*Context)(nil)) //注入请求参数值
  5. c.MapTo(c.rw, (*http.ResponseWriter)(nil))
  6. c.Map(req)
  7. return c
  8. }

run进行路由处理。

  1. func (c *context) run() {
  2. for c.index <= len(c.handlers) { // 遍历handlers,handlers实际为例子中使用USE注册的logger,recovery。当左右相等时,则调用m.action,及路由处理器
  3. _, err := c.Invoke(c.handler())
  4. if err != nil {
  5. panic(err)
  6. }
  7. c.index += 1 // 计数器,用来遍历注册的路由器
  8. if c.Written() {
  9. return
  10. }
  11. }
  12. }

run首先调用第一个注册的Logger。

  1. func Logger() Handler {
  2. return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) {
  3. start := time.Now()
  4. addr := req.Header.Get("X-Real-IP")
  5. if addr == "" {
  6. addr = req.Header.Get("X-Forwarded-For")
  7. if addr == "" {
  8. addr = req.RemoteAddr
  9. }
  10. }
  11. log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr)
  12. rw := res.(ResponseWriter)
  13. c.Next() //调用第二个注册的recovery
  14. log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start))
  15. }
  16. }

Martini一个神奇的地方在于Next这个函数,log,recovery记忆Handle通过next实现嵌套调用。

其次调用Recovery

  1. func Recovery() Handler {
  2. return func(c Context, log *log.Logger) {
  3. defer func() { // 捕获路由处理panic
  4. if err := recover(); err != nil {
  5. stack := stack(3)
  6. log.Printf("PANIC: %s\n%s", err, stack)
  7. // Lookup the current responsewriter
  8. val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
  9. res := val.Interface().(http.ResponseWriter)
  10. // respond with panic message while in development mode
  11. var body []byte
  12. if Env == Dev {
  13. res.Header().Set("Content-Type", "text/html")
  14. body = []byte(fmt.Sprintf(panicHtml, err, err, stack))
  15. } else {
  16. body = []byte("500 Internal Server Error")
  17. }
  18. res.WriteHeader(http.StatusInternalServerError)
  19. if nil != body {
  20. res.Write(body)
  21. }
  22. }
  23. }()
  24. c.Next() // 调用Handle进行请求路由处理
  25. }
  26. }

router.Handle路由处理

  1. func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
  2. fmt.Println(reflect.TypeOf(r.getRoutes()))
  3. bestMatch := NoMatch
  4. var bestVals map[string]string
  5. var bestRoute *route
  6. for _, route := range r.getRoutes() {
  7. match, vals := route.Match(req.Method, req.URL.Path)
  8. fmt.Println(match, vals)
  9. if match.BetterThan(bestMatch) {
  10. bestMatch = match // 遍历查找匹配的路径路由
  11. bestVals = vals
  12. bestRoute = route
  13. if match == ExactMatch {
  14. break
  15. }
  16. }
  17. }
  18. if bestMatch != NoMatch {
  19. fmt.Println("bestvals", bestVals)
  20. params := Params(bestVals)
  21. context.Map(params)
  22. bestRoute.Handle(context, res) // 路由器逻辑处理
  23. return
  24. }
  25. // no routes exist, 404
  26. c := &routeContext{context, 0, r.notFounds} //没有匹配路由时,执行默认的处理函数。notfind在初始化时注册
  27. context.MapTo(c, (*Context)(nil))
  28. c.run()
  29. }

路由业务逻辑处理route.Handle

  1. func (r *route) Handle(c Context, res http.ResponseWriter) {
  2. context := &routeContext{c, 0, r.handlers} // 初始化context,例子中对于/hello路径的请求r.handlers =[test2,test1]
  3. c.MapTo(context, (*Context)(nil))
  4. c.MapTo(r, (*Route)(nil))
  5. context.run() // 路由业务逻辑处理,遍历r.handlers获取业务逻辑方法
  6. }

run业务逻辑处理

  1. func (r *routeContext) run() {
  2. for r.index < len(r.handlers) {// 遍历handlers,实际实现时可以重写这一方法,自定义要调用方法的顺序。通过设置index可以自定义调用
  3. handler := r.handlers[r.index]
  4. vals, err := r.Invoke(handler)
  5. if err != nil {
  6. panic(err)
  7. }
  8. r.index += 1
  9. // if the handler returned something, write it to the http response
  10. //如果r.handlers的函数有返回值,则把返回值写入resp并设置r.writern()的返回值为true
  11. if len(vals) > 0 {
  12. ev := r.Get(reflect.TypeOf(ReturnHandler(nil)))
  13. handleReturn := ev.Interface().(ReturnHandler)
  14. handleReturn(r, vals)
  15. }
  16. if r.Written() {
  17. return
  18. }
  19. }
  20. }

实际使用Martini框架时,也可以根据需要自定义重写martini结果。重写martini结构后续的流程处理也是类似的。

来自为知笔记(Wiz)

时间: 2024-11-05 17:23:21

Martini 框架的相关文章

『Golang』Martini框架入门

本文介绍golang中的优秀web开发框架martini! 序 Martini框架是使用Go语言作为开发语言的一个强力的快速构建模块化web应用与服务的开发框架.Martini是一个专门用来处理Web相关内容的框架,其并没有自带有关ORM或详细的分层内容.所以当我们使用Martini作为我们的开发框架时,我们还需要选取适合的ORM等其他包. 安装 go get github.com/codegangsta/martini 使用 我们可以使用如下的代码来测试我们安装的包是否是可用的: // ser

Golang开发环境搭建(Notepad++、LiteIDE两种方式以及martini框架使用)

本文介绍两种Golang的开发环境一种基于notepad++.另一种基于liteide. 1.下载Golang语言的pkg:http://golangtc.com/download 直接点击安装,一路next. 2.程序员必备神器notepad++开发Golang环境很简单 一次点击:插件->Plugin Manger->Show Plugin Manger,安装插件GOnpp,重启notepad++. 新建文件命名为hello.go用notepad++打开,拷贝如下代码: package m

golang martini 源码阅读笔记之inject

martini是go语言写的一个超级轻量的web开源框架,具体源码可在github搜索找到.13年那会开始接触go语言时有稍微看过这个框架,由于之后没有继续使用go就慢慢忽略了,最近由于手头项目可能会用到,因此又想起这个框架. github上显示该项目更新不断,说明真是个好框架,简洁高效的东西从来都不缺少拥护者.周末阅读martini源码时做了注释写下一些理解,主要是inject.go以及martini.go两个文件,后续估计还会再阅读路由功能的主要文件. 注:以下的'泛型'均表示interfa

golang 依赖控制反转(IoC)

主流开发语言,为了达到项目间的低耦合,都会借助IoC框架来实现.即抽象和实现分离,使用抽象层,不用关心这些抽象层的具体实现:抽象层的实现,可以独立实现.现在比较流行的领域驱动设计(ddd),为了达到将领域层作为最核心,也需要依赖于IOC. 回过头来,我们看看golang实现的ioc框架,有golang风格的框架,也有从其他主流语言搬过来的比较重的框架.我觉得目前实现最轻量级的,当属martini框架的ioc依赖库 github.com/codegangsta/inject  .代码行数很少,提供

[Golang]你处于使用Go语言的哪个层次,来测测吧

?? Francesc (@francesc) 是 Go 核心团队的一员, 是提倡 Google Cloud 平台的开发者. 他是一个编程语言的爱好者, Google的技术指导大师, Go tour的创造者之一. 这个讨论的灵感来自于另一个 Raquel Vélez 在 JSConf. Slides 的讨论,这个讨论已经发到了这里. Sourcegraph 是下一代编程协作工具, 用于搜索, 探索, 和审查代码. 我们参加GopherCon India 来分享我们是怎样使用 Go 并学习别人是怎

豆瓣Redis解决方案Codis源码剖析:Dashboard

豆瓣Redis解决方案Codis源码剖析:Dashboard 1.不只是Dashboard 虽然名字叫Dashboard,但它在Codis中的作用却不可小觑.它不仅仅是Dashboard管理页面,更重要的是,它负责监控和指挥各个Proxy的负载均衡(数据分布和迁移).并且,所有API都以RESTFul接口的形式对外提供,供Proxy和codis-config(Codis的命令行工具)调用.下面就来看一下数据分布和迁移的代码执行流程. Dashboard涉及到的知识点比较多,包括Martini框架

澳门赌博攻略,整体说来Backbone的events模块还是很不错的

我们是通过判断请求的路径来直接返回结果的.简单粗暴,缺点明显:如果url后面加杂了queryString,因为判断逻辑中没有处理,那么将直接返回404页面(其实也没有其他的页面). 难道要一个一个加queryString的处理?有可行性,但麻烦. 再者,如果要添加新的html页面,那么也要在处理逻辑中依次添加?html页面还要引用js脚本,css样式,样式表中还要引用图片,字体....难道就要这样无休止的添加下去?显然是不可能的. 借助于NodeJS提供的urlAPI,我们可以提取请求url中的

开源一个golang小程序商城后台(moshopserver)

开源一个golang小程序商城后台(moshopserver) golang和c/c++比起来是一门新的语言,一直想学,网上搜集了一些资料,有些人说很容易上手,确实是这样,和C/C++比起来,少了很多乱七八糟的语法.学一门新的语言,最好的方法就是动手写一些东西,最近小程序也比较火,也想学一下,网络上搜索的一些开源项目,基本上没有golang实现的,大部分都是nodejs和java写的,那么我就来实现一个golang版的吧,一石二鸟. 开发小程序前后端都需要开发,自己的前端经验很少,搜索了一些开源

15. Go 语言“避坑”与技巧

Go 语言"避坑"与技巧 任何编程语言都不是完美的,Go 语言也是如此.Go 语言的某些特性在使用时如果不注意,也会造成一些错误,我们习惯上将这些造成错误的设计称为"坑". Go 语言的一些设计也具有与其他编程语言不一样的特性,能优雅.简单.高效地解决一些其他语言难以解决的问题. 本章将会对 Go 语言设计上可能发生错误的地方及 Go 语言本身的使用技巧进行总结和归纳. goroutine(Go语言并发)如何使用才更加高效? Go语言原生支持并发是被众人津津乐道的特