gin 中间件
gin的中间件,源码层面上还是一个gin.HandlerFunc. 只不过中间件的HandlerFunc放前面,Router的Handler放最后面就是了。 gin中间件的通常写法如下所示:
func MiddleXXX() gin.HandlerFunc {
var logger *log.Logger
return func(c *Context) {
// 中间件逻辑
c.Next()
}
}
gin.Context
对于每一个请求, gin.Context 都是独一无二的,因此可以利用context来传递一些信息,比如在中间件处理中写入保存请求头里的一些信息。获取调用链追踪的时候,写入TraceID等等。 Context可以重用。gin.Pool会在是请求结束之后回收Context. 项目中可以用 type HeaderKey struct {} 定义Context的键,这种写法的好处是无需额外的内容分配,go底层的内存管理对于0Byte的结构体做了特殊处理。
gin 请求解析分析
gin在解析请求之后,会将解析到的 handlers 保存在 gin.Context.handlers 里,也会记录 context.fullPath 值。 然后就是遍历 handlers 顺序调用。最后把要写的信息,写入到连接的输出中 c.writermem.WriteHeaderNow()
c.JSON () 到底做了啥?
func (c *Context) JSON(code int, obj any) {
c.Render(code, render.JSON{Data: obj})
}
render.JSON.Data 类型是any,因此可以赋予任何类型的值。 如果输出格式是JSON,底层调用的是 WriteJSON()方法。Content-Type 会写入:application/json; charset=utf-8 要写入的数据会通过 json.Marshal 编码。然后调用 Write 写入字节数据。
未完待续。。。
gin如果解析 applicaiton/json 请求参数
- 首先会根据请求头部的 Content-Type 判断是否是 applicaiton/json 类型
- 参数绑定工作交由jsonBinding结构体来处理。http.Request.Body 包含原始的请求信息
- 具体实现,参考如下代码:
- gin-gonic/[email protected]/binding/json.go
func decodeJSON(r io.Reader, obj any) error {
decoder := json.NewDecoder(r)
if EnableDecoderUseNumber {
decoder.UseNumber()
}
if EnableDecoderDisallowUnknownFields {
decoder.DisallowUnknownFields()
}
if err := decoder.Decode(obj); err != nil {
return err
}
return validate(obj)
}
注意 [email protected]/internal/json/json.go 头部的声明
//go:build !jsoniter && !go_json
// +build !jsoniter,!go_json
可以用标准库的 encoding/json (默认) , 也可以用 gojson 或者 build . 在构建程序的按如下方式构建即可
go build -tags=jsoniter .
go build -tags=go_json .
gojson 和 jsoniter 性能比标准库的好一些,实测只是快2,3倍,并没有宣传的10倍那么夸张。。。
[email protected]/ginS/gins.go 实现单例engine方案。
- 需用用到标准库 sync.Once
- 需要定义一个外部不可访问的变量 internalEngine
var once sync.Once
var internalEngine *gin.Engine
func engine() *gin.Engine {
once.Do(func() {
internalEngine = gin.Default()
})
return internalEngine
}