跟着Gin案例学习源码-AsciiJSON

雪花还在飘落,浓雾还没散去,我仍然在行走。我在疲惫里越走越深,我想坐下来,然后就坐下了。我不知道是坐在椅子里,还是坐在石头上。我的身体摇摇晃晃坐在那里,像是超重的货船坐在波动的水面上。------《第七天》

官方案例

AsciiJSON

Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters.

go 复制代码
func main() {
	r := gin.Default()

	r.GET("/someJSON", func(c *gin.Context) {
		data := map[string]interface{}{
			"lang": "GO语言",
			"tag":  "<br>",
		}

		// will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
		c.AsciiJSON(http.StatusOK, data)
	})

	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

将其放入代码进行调试

源码执行流程

graph LR 案例-->Gin.Default Gin.Default--> New Gin.Default--> engine.Use Gin.Default--> engine.With 案例--> r.GET --> group.handle group.handle --> group.calculateAbsolutePath --> joinPaths group.handle --> group.combineHandlers group.handle --> group.engine.addRoute --> root.addRoute 案例--> r.Run r.Run --> resolveAddress r.Run --> http.ListenAndServe

Default

打入断点,当函数 Default 执行后,r 变量返回了一个 *gin.Engine

进入 Default 函数,函数返回一个 *Engine,函数返回结果是 `engine.With(opts...)

go 复制代码
func Default(opts ...OptionFunc) *Engine {  
    debugPrintWARNINGDefault()  
    engine := New()  
    engine.Use(Logger(), Recovery())  
    return engine.With(opts...)  
}

New

可以看到,engine 是通过 New 函数创建的

go 复制代码
func New(opts ...OptionFunc) *Engine {  
    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(engine.maxParams)  
    }    
    return engine.With(opts...)  
}

func (engine *Engine) With(opts ...OptionFunc) *Engine {  
    for _, opt := range opts {  
       opt(engine)  
    }  
    return engine  
}

来到 New 函数实现可以看到,它创建了一个新的 Engine,将 engine 中的属性 RouterGroup.engine 指向自己,最后返回 engine.With(opts...)

engine.Use

将全局中简介添加到 group.Handlers 上

go 复制代码
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {  
    group.Handlers = append(group.Handlers, middleware...)  
    return group.returnObj()  
}

engine.With

go 复制代码
func (engine *Engine) With(opts ...OptionFunc) *Engine {  
    for _, opt := range opts {  
       opt(engine)  
    }  
    return engine  
}

r.GET

go 复制代码
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {  
    return group.handle(http.MethodGet, relativePath, handlers)  
}

group.handle

go 复制代码
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()  
}

这里关键在于 group.engine.addRoute(httpMethod, absolutePath, handlers)

可以看到按照例子执行后,此时 absolutePath 值为 /someJSON

addRoute

go 复制代码
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)  
  
    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)  
  
    if paramsCount := countParams(path); paramsCount > engine.maxParams {  
       engine.maxParams = paramsCount  
    }  
  
    if sectionsCount := countSections(path); sectionsCount > engine.maxSections {  
       engine.maxSections = sectionsCount  
    }  
}

进入 addRoute 后,重点在 engine.trees 的处理,处理完毕后的 trees 为:

执行完成后,使用 r.Run 启动服务,自此,服务初始化启动完成。

相关推荐
向生18 分钟前
Ubuntu Caddy 保姆级完整教程
后端
tntxia24 分钟前
Docker权限的问题
后端
明月_清风25 分钟前
看完这段关于"全插件化架构"的技术分析后,我整理了一份笔记
前端·后端
摇滚侠1 小时前
《SpringBoot 3:入门与应用实战》第 7 章 AOP 思想与实现 阅读笔记 14
spring boot·笔记·后端
她的男孩1 小时前
我把管理系统接给AI,它改条数据都要先问我
java·后端·架构
元界metalite2 小时前
SpringBoot分页接口怎么设计-pageSize不设上限会发生什么
后端
IT爱学堂3 小时前
Go开发疑难杂症终结者通关指南
后端
大勇前进3 小时前
从10分钟到10秒:一个真实慢查询的SQL Server优化全记录
后端
站大爷IP3 小时前
被Python异步坑了三年,才搞懂await和create_task的区别
后端
大白803 小时前
你的数据库密码还在用明文?SQL Server 凭据管理的 3 种现代方案
后端