新公司用的 go router 框架是 go-martini/martini,github 有 11.2k 的 start,是一个非常优秀的项目,但是个人觉得文档确实写的简单了点。
根据文档中间件的使用有 2 种方法,我总结为通过 Handlers
函数方法和非 Handlers
函数方法,其实主要区别就是 Handlers
函数方法优先级更高,它将会替换掉之前的任何设置过的中间件,但是组中的中间件还是会执行,但是组中的中间件还是会执行,但是组中的中间件还是会执行。
package main
import (
"github.com/go-martini/martini"
"fmt"
)
func main() {
m := martini.Classic()
m.Use(func(c martini.Context) {
fmt.Println("第 1 个中间件 before a request")
})
m.Use(func() {
fmt.Println("第 2 个中间件 111111111111")
})
m.Use(func(c martini.Context) {
fmt.Println("第 3 个中间件 group middleware")
})
m.Group("/api", func(r martini.Router) {
r.Get("/test", func() string {
return "...... return success ....."
})
}, func() {
fmt.Println("第 4 个中间件 group middleware")
})
m.RunOnAddr(":8081")
}
以上是我写的一个例子,可以看下请求后的打印结果:
可以看到是按着代码顺序执行的。
我们可以把上面代码稍微改下,如下:
package main
import (
"github.com/go-martini/martini"
"fmt"
)
func main() {
m := martini.Classic()
m.Use(func(c martini.Context) {
fmt.Println("第 1 个中间件 before a request")
})
m.Use(func() {
fmt.Println("第 2 个中间件 111111111111")
})
m.Use(func(c martini.Context) {
fmt.Println("第 3 个中间件 group middleware")
})
m.Handlers(
func() {
fmt.Println("其他中间件都失效,只有我执行了..")
},
)
m.Group("/api", func(r martini.Router) {
r.Get("/test", func() string {
return "...... return success ....."
})
}, func() {
fmt.Println("第 4 个中间件 group middleware")
})
m.RunOnAddr(":8081")
}
执行后打印结果如下:
说明只执行了 Handlers
函数中的中间件和 group 组中的中间件。
Context.Next()是一个可选的函数用于中间件处理器暂时放弃执行直到其他的处理器都执行完毕. 这样就可以很好的处理在http请求完成后需要做的操作.
对于Next()
函数不要问我原理,我不知道原理,原理实现以后再说吧。
对于 Next 我觉得可以理解成遇到 Next()
后就入栈,先进后出。
以下我也是通过有没有 Handlers
函数中间件来举例。
package main
import (
"github.com/go-martini/martini"
"fmt"
)
func main() {
m := martini.Classic()
m.Use(func(c martini.Context) {
fmt.Println("第 1 个中间件 before a request")
c.Next()
fmt.Println("第 6 个中间件 after a request") // 入栈等待处理
})
m.Use(func() {
fmt.Println("第 2 个中间件 111111111111")
})
m.Use(func(c martini.Context) {
fmt.Println("第 3 个中间件 group middleware")
c.Next()
fmt.Println("第 5 个中间件 group middleware") // 入栈等待处理
})
m.Group("/api", func(r martini.Router) {
r.Get("/test", func() string {
fmt.Println("......last 最后执行........")
return "...... return success ....."
})
}, func() {
fmt.Println("分组中的中间件 group middleware")
})
m.RunOnAddr(":8080")
}
执行后的打印结果如下:
请注意第 5 和第 6 个中间件是最后执行的,是在 http请求完成后 执行的。
package main
import (
"github.com/go-martini/martini"
"fmt"
)
func main() {
m := martini.Classic()
m.Handlers(
func() {
fmt.Println("第 1 次执行....")
},
func(c martini.Context) {
c.Next()
fmt.Println("http 请求完成后执行....")
},
func() {
fmt.Println("第 2 次执行....")
},
)
m.Group("/api", func(r martini.Router) {
r.Get("/test", func() string {
fmt.Println("......last........")
return "...... return success ....."
})
}, func() {
fmt.Println("组中的中间件 group middleware")
})
m.RunOnAddr(":8081")
}
服务是我看了好半天都没看懂的点,尤其是什么是映射值到接口,真的不是太明白一开始。
服务即是被注入到处理器中的参数. 你可以映射一个服务到 全局 或者 请求 的级别.
关于服务最强悍的地方之一就是它能够映射服务到接口. 例如说, 假设你想要覆盖http.ResponseWriter成为一个对象, 那么你可以封装它并包含你自己的额外操作, 你可以如下这样来编写你的处理器:
其实服务就是参数,参数可以是简单数据类型或是复杂复杂数据类。
可以映射一个服务到 全局 或者 请求 的级别
其实也就是这个参数可以使用的范围或者叫作用域。
package main
import (
"github.com/go-martini/martini"
"fmt"
"net/http"
"time"
)
type ReqContext struct {
ExString string
Req *http.Request
}
func myHandle(reqCtx *ReqContext) string {
return fmt.Sprintf(`handle return....%s, ex val= :%s `, reqCtx.Req.URL.Query().Encode(), reqCtx.ExString)
}
func registerMyRouter(r martini.Router) {
r.Group("/api", func(r martini.Router) {
r.Get("/test", myHandle)
}, func() {
fmt.Println("组中的中间件 group middleware")
})
}
func main() {
m := martini.Classic()
m.Use(func(req *http.Request, c martini.Context) {
reqC := &ReqContext{
ExString: "1112323 " + time.Now().Format("2006-01-02 15:04:05"),
Req: req,
}
c.Map(reqC)
})
registerMyRouter(m)
m.RunOnAddr(":8083")
}
这是一个简单的全局映射的服务,那么 ReqContext 这个服务(参数) 将可以在所有的处理器中被使用到。我们的 myHandle() 处理器(方法) 就直接用了,且没有报错。执行后打印的结果如下:
package main
import (
"github.com/go-martini/martini"
"fmt"
"net/http"
"time"
)
type ReqContext struct {
ExString string
Req *http.Request
}
func do(reqCtx *ReqContext) string {
return fmt.Sprintf(`handle return....%s, ex val= :%s `, reqCtx.Req.URL.Query().Encode(), reqCtx.ExString)
}
func myHandle(req *http.Request, c martini.Context) {
reqC := &ReqContext{
ExString: "1112323 " + time.Now().Format("2006-01-02 15:04:05"),
Req: req,
}
c.Map(reqC)
}
func myHandle2(reqCtx *ReqContext) string {
return fmt.Sprintf(`handle return....%s, ex val= :%s `, reqCtx.Req.URL.Query().Encode(), reqCtx.ExString)
}
func myHandle3(req *http.Request) string {
return fmt.Sprintf(`handle return....%s`, req.URL.Query().Encode())
}
func registerMyRouter(r martini.Router) {
r.Group("/api", func(r martini.Router) {
r.Get("/test", myHandle, do)
r.Get("/test2", myHandle2)
r.Get("/test3", myHandle3)
}, func() {
fmt.Println("组中的中间件 group middleware")
})
}
func main() {
m := martini.Classic()
registerMyRouter(m)
m.RunOnAddr(":8085")
}
执行后打印结果如下:
这是一个简单的请求级别的映射,我们对 /api/test 做了映射,所以在 /api/test 请求中 ReqContext 参数是全局可以用,这也就是 do() 方法中的参数 do(reqCtx *ReqContext)
中的来历。
在 /api/test3 中我们没有用任何其他的参数,可以正确执行请求。
在 /api/test2 中我们用到了 ReqContext 参数,这是 /api/test 请求映射的,只能在 /api/test 中使用,这就是报错是原因。
关于服务最强悍的地方之一就是它能够映射服务到接口. 例如说, 假设你想要覆盖http.ResponseWriter成为一个对象, 那么你可以封装它并包含你自己的额外操作, 你可以如下这样来编写你的处理器:
我个人理解的就是一种类型覆盖。
package main
import (
"github.com/go-martini/martini"
"fmt"
"net/http"
"time"
)
type ReqContext struct {
Svr interface{
}
ExString string
Req *http.Request
}
type ServiceGetXIImpl interface {
GetXI()
}
type Tmp struct {
}
func (s *Tmp) GetXI() {
fmt.Println("tmp GetXI()")
}
type Service struct {
Name string
Age int
}
func (s *Service) printXX() {
fmt.Println("我是 service printXX() 方法")
}
func (s *Service) GetXI() {
fmt.Println("我是 Service 实现了 ServiceGetXIImpl 接口中的 GetXI() 方法")
}
func myHandle(reqCtx *ReqContext) string {
fmt.Printf("最后 myHandle 中的 reqCtx.Svr 类型为 %T \n ", reqCtx.Svr)
return fmt.Sprintf(`handle return....%s, ex val= :%s `, reqCtx.Req.URL.Query().Encode(), reqCtx.ExString)
}
func registerMyRouter(r martini.Router) {
r.Group("/api", func(r martini.Router) {
r.Get("/test", myHandle)
}, func() {
fmt.Println("组中的中间件 group middleware")
})
}
func ExecMapTo(ctx *ReqContext) *ReqContext {
ctx.Svr = &Service{
}
return ctx
}
func WrapMapTo(reqCtx *ReqContext, c martini.Context) {
fmt.Printf("MapTo 前 Svr 类型为: %T \n", reqCtx.Svr)
rw := ExecMapTo(reqCtx)
c.MapTo(rw, (*ServiceGetXIImpl)(nil)) // 覆盖 reqCtx.Svr 的 Tmp 类型为 Service
fmt.Printf("MapTo 后 Svr 类型为: %T \n", reqCtx.Svr)
}
func main() {
m := martini.Classic()
m.Use(func(req *http.Request, c martini.Context) {
reqC := &ReqContext{
Svr: Tmp{
},
ExString: "1112323 " + time.Now().Format("2006-01-02 15:04:05"),
Req: req,
}
c.Map(reqC)
})
m.Use(func(reqCtx *ReqContext, c martini.Context) {
WrapMapTo(reqCtx, c)
})
registerMyRouter(m)
m.RunOnAddr(":8086")
}
这个例子主要是想把 ReqContext 中的 Svr 从 Tmp 类型替换为 Service 类型。
执行后的打印结果如下:
可以从结果中看出,确实符合我们的预期。
综上,欢迎指正。