gin是用go语言编写的一个http web框架
官方网站:https://gin-gonic.com/zh-cn/
一、gin 安装
$ go get github.com/gin-gonic/gin
二、Hello World
1、代码
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
2、运行
go run main.go
运行main.go打印日志:
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /ping --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
3、访问
浏览器访问 http://localhost:8080/ping 响应如下:
{"message":"pong"}
三、源码分析
1、r := gin.Default()
1、跟进 gin.Default()
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
// 此处可以看到程序启动时打印的日志:[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
debugPrintWARNINGDefault()
// 初始化引擎
engine := New()
// 使用默认日志打印及异常处理组件
engine.Use(Logger(), Recovery())
return engine
}
2、跟进 engine := New()
func New() *Engine {
// 此处可以看到程序启动时打印的日志
// [WARNING] Running in "debug" mode. Switch to "release" mode in production.
// using env: export GIN_MODE=release
// using code: gin.SetMode(gin.ReleaseMode)
debugPrintWARNINGNew()
// 初始化引擎
engine := &Engine{
// 路由组
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
TrustedPlatform: defaultPlatform,
UseRawPath: false,
RemoveExtraSlash: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJSONPrefix: "while(1);",
trustedProxies: []string{"0.0.0.0/0", "::/0"},
trustedCIDRs: defaultTrustedCIDRs,
}
// 设置路由组引擎为本引擎
engine.RouterGroup.engine = engine
engine.pool.New = func() any {
return engine.allocateContext()
}
return engine
}
3、路由组定义:
type RouterGroup struct {
Handlers HandlersChain
basePath string
engine *Engine
root bool
}
2、r.GET()
1、跟进 r.GET(“/ping”, func(c *gin.Context),可以看到GET()方法的接收器是group *RouterGroup
// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
2、跟进 group.handle(http.MethodGet, relativePath, handlers)
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
// 绑定路由及处理器函数
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
3、跟进group.engine.addRoute(httpMethod, absolutePath, handlers)
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
assert1(path[0] == '/', "path must begin with '/'")
assert1(method != "", "HTTP method can not be empty")
assert1(len(handlers) > 0, "there must be at least one handler")
debugPrintRoute(method, path, handlers)
// 获取请求http请求方法root
root := engine.trees.get(method)
if root == nil {
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
// 绑定路由及请求处理器函数
root.addRoute(path, handlers)
// Update maxParams
if paramsCount := countParams(path); paramsCount > engine.maxParams {
engine.maxParams = paramsCount
}
if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
engine.maxSections = sectionsCount
}
}
3、r.Run()
1、跟进r.Run()
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
if engine.isUnsafeTrustedProxies() {
// 此处可以看到程序启动时打印的日志:
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
}
// 此处可以看到程序启动时打印的日志: Environment variable PORT is undefined. Using port :8080 by default
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
// 此处为go语言的ListenAndServe
err = http.ListenAndServe(address, engine.Handler())
return
}
2、跟进engine.Handler(),可以看到请求处理器就是 Engine
func (engine *Engine) Handler() http.Handler {
if !engine.UseH2C {
return engine
}
h2s := &http2.Server{}
return h2c.NewHandler(engine, h2s)
}
3、既然请求处理器是 Engine,那么 Engine就实现了ServeHTTP(w http.ResponseWriter, req *http.Request) ,跟踪代码可以看到其实现方式:
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
// 请求处理
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
4、跟进engine.handleHTTPRequest(c ),已删除部分代码
func (engine *Engine) handleHTTPRequest(c *Context) {
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method != httpMethod {
continue
}
root := t[i].root
// 根据请求路由找到GET函数注册的请求处理器函数
// Find route in tree
value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
if value.params != nil {
c.Params = *value.params
}
// 遍历请求处理器
if value.handlers != nil {
c.handlers = value.handlers
c.fullPath = value.fullPath
// 执行请求处理器
c.Next()
c.writermem.WriteHeaderNow()
return
}
}
5、跟进c.Next()可以看到 Hello World 中GET绑定的请求处理器函数的执行
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
// 执行Hello World 中GET绑定的请求处理器函数
c.handlers[c.index](c)
c.index++
}
}