跟着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 启动服务,自此,服务初始化启动完成。

相关推荐
郑祎亦24 分钟前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
本当迷ya37 分钟前
💖2025年不会Stream流被同事排挤了┭┮﹏┭┮(强烈建议实操)
后端·程序员
计算机毕设指导62 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
paopaokaka_luck2 小时前
[371]基于springboot的高校实习管理系统
java·spring boot·后端
捂月3 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
煎鱼eddycjy3 小时前
新提案:由迭代器启发的 Go 错误函数处理
go
煎鱼eddycjy3 小时前
Go 语言十五周年!权力交接、回顾与展望
go
瓜牛_gn3 小时前
依赖注入注解
java·后端·spring
Estar.Lee4 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪4 小时前
Django:从入门到精通
后端·python·django