gin

package module
v0.0.0-...-6668abe Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2024 License: MIT Imports: 37 Imported by: 0

README

Gin 源码阅读

本次的代码版本使用 v1.9.1

加了注释的gin代码地址: https://github.com/gofish2020/gin

演示范例代码路径 https://github.com/gofish2020/gin/examples

前置知识

Gin源码整体上逻辑还算简单,主要的逻辑就是围绕着压缩前缀树这个数据结构。如果从来没有听说过这个,问题不大,我们先一起了解下。

这里先简单说下前缀树是干什么的?本质就是用来做字符串匹配的。

比如有这么几个单词: tea team term test。如果我们要查找 test这个单词是否存在,那就要一个单词一个单词的比较,即可知道结果。那如果要查找 eat这个单词,很显然我们肉眼知道是不存在的,但是代码中也还是要一个个的比较才能知道。

那如何能快速的知道是否存在呢? 那就把这些单词存储形式调整下(不是一个个单词的保存),而是保存到树中(每一个字符就是一个树中的节点)。 如下图:

从上面的树结构可以发现,相同的前缀 te只存储了一份,不像一个个单词存储的时候,每个单词都保存了一份te,相对来说节约了存储空间(压缩前缀树可以进一步优化空间),那么这就是前缀树名字的由来。

当我们有了上面的这个多叉树的根节点,如果要查找 eat单词,从上图可知,根节点下面只有t字符,没有e字符,所以立刻就知道了eat不存在。

当我们要查找 test单词,从根节点,我们可以找到t,从t继续向下可以找到e,以此类推,顺着这个树我们可以找到并且存储着test字符串的节点。

这时如果我们要查找 te 单词,从根节点开始,可以顺利的找到 te两个字符,但是节点中并没有存储te字符串,所以te不存在。

Leetcode中正好有实现前缀树的算法题,大家可以尝试下 https://leetcode.cn/problems/implement-trie-prefix-tree/description/

算法答案如下:

type Trie struct {
    next [26]*Trie // 26个字母(26叉树)
    isLast bool // 表示【当前节点】是不是字符串的最后一个字母
}

func Constructor() Trie {
   return Trie{}
}

// 保存字符串
func (this *Trie) Insert(word string)  {

    for _, w := range word { // 遍历字符串
        // 在树中判断字母
        node := this.next[ w -'a']
        if node == nil {
            node = new(Trie)
            this.next[w-'a'] = node
        }
        this = node // 让this指向新节点
    }

    this.isLast = true // 表示当前this最后指向的位置,就是字符串的最后一个字母
}

// 搜索字符串
func (this *Trie) Search(word string) bool {

    for _,w := range word {
        node := this.next[w-'a']
        if node == nil { // 说明没有找到字母
            return false
        }

        // 找到了,继续下一个字母
        this = node
    }
    // 例如 :先插入 apple ,然后搜索app,确实可以搜到,但是app不是有效的字符串(因为没有插入app),只是恰好和apple的前半部分相同而已(就是要求完全匹配)
    return this.isLast
}

// 搜索前缀
func (this *Trie) StartsWith(prefix string) bool {
    for _,w := range prefix {
        node := this.next[w-'a']
        if node == nil { // 说明没有找到字母
            return false
        }
        // 找到了,继续下一个字母
        this = node
    }
    // 因为只是找前缀,并不是要求完全匹配
    return true
}

那么压缩前缀树是什么?

比如前面的公共前缀 te 这两个字符,完全可以一起保存为 te,不用分成两个单独的字符存储,如此不就又可以减少存储空间了嘛。(本质就是对前缀树存储空间上的更进一步的优化)

但是在具体的代码实现上比上面的前缀树要复杂些,我们还是以实际的Gin中的源码并结合图片的方式给大家讲解清楚。具体怎么实现的请看下面的源码阅读。

源码阅读

基本范例

在讲解源码之前先看下基本的使用范例,对gin有个大概的认知。 源码阅读也是围绕下面的函数展开。

package main

import (
	"fmt"
	"net/http"

	"github.com/gofish2020/gin"
)

func handlerTest1(c *gin.Context) {
	c.String(http.StatusOK, c.Request.URL.Path)
}

func main() {
	// 初始化 *Engine 对象
	router := gin.New()

	router.GET("/a", handlerTest1)  // 路径 /a
	router.GET("/b", handlerTest1)  // 路径 /b
	router.GET("/", handlerTest1) // 路径 /

	// 这里相当于公共的前缀 /ab
	group := router.Group("/ab")
	{
		group.GET("/a", handlerTest1) // 路径 /ab/a
		group.GET("/b", handlerTest1) // 路径 /ab/b
	}
	// "/static" 表示访问的路由
	// "." 访问的文件在服务器的存储目录
	// 访问范例: http://127.0.0.1:8080/static/test.txt
	router.Static("/static", ".")

	// 遍历压缩前缀树
	list := router.Routes()
	for _, l := range list {
		fmt.Println(l.Method, l.Path)
	}

	// 启动服务
	router.Run(":8080")
}


函数阅读

下面的代码我会只保留重要的部分,不重要的部分会省略,便于大家看重点。 方法树就是压缩前缀树,直接用methodTree直译名字来称呼。

gin.New() 函数

gin.New()函数创建 *Engine对象;并且初始化了RouterGroup trees pool 三个对象。

// *Engine的本质,其实是对 RouterGroup的额外包装,真正调用执行的方法,其实都是RouterGroup的
func New() *Engine { 
	
	engine := &Engine{
		RouterGroup: RouterGroup{
			Handlers: nil,
			basePath: "/",
			root:     true,
		},

		trees:                  make(methodTrees, 0, 9), // 9个方法树
	}
	engine.RouterGroup.engine = engine // 这里设定 engine 到 RouterGroup中;相当于 engine中可以知道RouterGroup,同时RouterGroup 可以知道engine。相互引用的效果。
	engine.pool.New = func() any {     // 用池子,来复用 *Context 对象
		return engine.allocateContext(engine.maxParams)
	}
	return engine
}

疑问点:为什么 trees 这个切片的大小是 9?

可以看下这个库文件 /net/http/method.go,因为标准的 http请求方法就是9种

package http

// Common HTTP methods.
//
// Unless otherwise noted, these are defined in RFC 7231 section 4.3.
const (
	MethodGet     = "GET"
	MethodHead    = "HEAD"
	MethodPost    = "POST"
	MethodPut     = "PUT"
	MethodPatch   = "PATCH" // RFC 5789
	MethodDelete  = "DELETE"
	MethodConnect = "CONNECT"
	MethodOptions = "OPTIONS"
	MethodTrace   = "TRACE"
)

methodTrees结构体

本质就是一个切片,而且切片的每个子元素类型为 methodTree

  • method string保存请求的方式(例如GET/POST/HEAD/DELETE等)
  • root *node 就是 该请求方法对应的压缩前缀树的根节点
type methodTree struct {
	method string
	root   *node
}

// 方法树对象:本质就是个切片
type methodTrees []methodTree

func (trees methodTrees) get(method string) *node {

	// 因为是切片,所以需要遍历的方式,查找 method 是否存在
	for _, tree := range trees {
		if tree.method == method {
			return tree.root
		}
	}
	return nil // 不存在返回nil
}
router.GET() 函数

上面的 New()函数创建好了 *Engine对象,接下来就要使用该对象router.GET()添加路由了。

进入 router.GET()函数可以看到,我们虽然表面上调用的 *Engine对象的 GET()函数,但是实际上是 RouterGroup.GET() 函数。


// 因为这种声明方式
type Engine struct {
	RouterGroup
    
}

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


func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodPost, relativePath, handlers)
}

然后继续进入 group.handle函数内部

  • 拼接 group.basePath + relativePath
  • 拼接 group.Handlers + handlers HandlersChain
  • 【重点】group.engine.addRoute 在方法树上添加 【路径】 和 【路径的处理函数】

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
	// 拼接 group.basePath + relativePath
	absolutePath := group.calculateAbsolutePath(relativePath)
	// 将 group.Handlers + handlers HandlersChain合并到一起,作为一个 HandlersChain
	handlers = group.combineHandlers(handlers)
	// 在方法树上添加 【路径】 和 【路径的处理函数】
	group.engine.addRoute(httpMethod, absolutePath, handlers)
	return group.returnObj()
}


// 拼接 basePath + relativePath
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
	return joinPaths(group.basePath, relativePath)
}


func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
	finalSize := len(group.Handlers) + len(handlers)
	// 这里的 abortIndex 固定值 63,意思就是 group.Handlers + handlers HandlersChain 的函数切片最多只能有62个
	assert1(finalSize < int(abortIndex), "too many handlers")
	mergedHandlers := make(HandlersChain, finalSize)
	copy(mergedHandlers, group.Handlers)                 // 将 group中的 Handlers
	copy(mergedHandlers[len(group.Handlers):], handlers) // 拼接上 handlers HandlersChain
	return mergedHandlers
}

这里重点看下 group.engine.addRoute函数内部逻辑

  • path method handlers做前置校验
  • 基于 method string engine.trees.get获取该方法对应的压缩前缀树的根节点(不存在,就是创建一个新的根节点)
  • 【重点】root.addRoute(path, handlers)在前缀树中存储 pathhandlers
// 在方法树中,添加 path 和 处理方法 handlers 之间的映射关系(注意: handlers是个方法切片,即一个path 对应一组处理方法)
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {

	// 要求 path第一个字符必须是 /
	assert1(path[0] == '/', "path must begin with '/'")
	// 要求 请求method不能为空
	assert1(method != "", "HTTP method can not be empty")
	// 要求 切片不能为空
	assert1(len(handlers) > 0, "there must be at least one handler")

	debugPrintRoute(method, path, handlers)

	// 从trees切片中,获取 method对应的树的根节点
	root := engine.trees.get(method)
	if root == nil { // 不存在

		// 创建根节点
		root = new(node)
		root.fullPath = "/"
		// 并保存到 trees中
		engine.trees = append(engine.trees, methodTree{method: method, root: root}) // 创建新的方法树
	}

	// 将 路径 + 该路径对应的处理函数,保存到方法树中(这里就是压缩前缀树的存储逻辑)
	root.addRoute(path, handlers)

	// 计算参数个数(也就是 : 和 * 开头的字符串个数)
	if paramsCount := countParams(path); paramsCount > engine.maxParams {
		engine.maxParams = paramsCount
	}

	// 计算path由几个部分组成(也就是 / 的个数)
	if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
		engine.maxSections = sectionsCount
	}
}

进入root.addRoute(path, handlers)函数内部,看下压缩前缀树是怎么存储字符串的

代码注释很清晰,但是如果第一次看估计有点晕。以添加 /a /b /这三个路径为例图示讲解下root.addRoute函数逻辑。(建议先看代码,再配合图理解)

router.GET("/a", handlerTest1) // 路径 /a
router.GET("/b", handlerTest1) // 路径 /b
router.GET("/", handlerTest1)  // 路径 /

向压缩前缀树中添加【 路径/a】

因为第一次添加,所以直接将路径/a保存到根节点中【代码注释1逻辑】

向压缩前缀树中添加【 路径 /b】

因为此时树的根节点已经保存了 /a(即 n.path的值),所以树就不是空树了。

此时进入for循环执行longestCommonPrefix函数【代码注释2逻辑】,求 n.path(即 /a)和 path(即 /b)的公共前缀长度,/a和/b的公共前缀为/,长度为1, 1的值小于 n.path(即 /a)的长度2,就需要将n指向的根节点分裂,将公共部分分裂出来(分裂为: / 和 a)【代码注释3逻辑】,这样等价于就把/a和/b两个字符串的公共部分/给分裂了出来。

此时只是完成了根节点的分裂,但是 /b 还没有保存进去。【代码注释4逻辑】 公共前缀的长度1 小于 path(即/b)的长度2,前面的分裂已经有了公共的前缀/节点,那么只需要将剩余的"b"放到 "/"节点后面不就可以了。

这个里面还有一个额外的逻辑,如果 / 节点后面,之前已经存在了 b开头的节点,会让n直接指向这个子节点,然后代码continue walk,让 n指向的b开头的节点 和 path(注意这里的path只剩下了b字符path = path[i:])重复for循环的逻辑。

向压缩前缀树中添加【 路径 /】

(每次插入路径都是从根节点开始)前面的根节点已经分裂出了 / 节点,此时和路径 / 比较,他们的公共前缀长度为1(即等于 n.path长度,也等于 path长度】,所以代码逻辑直接执行【代码注释5】,也就是n节点匹配到了字符串的尾部。

下面就是加了注释的代码(建议配合上面的添加过程阅读

node结构体定义

// 整个数据结构 就是一个多叉树
type node struct {
	path      string //  当前节点表示的【字符串值】
	indices   string // 当前节点下面的所有的子节点中的path值的首字母(用于快速判断,有哪些子节点)
	wildChild bool
	nType     nodeType
	priority  uint32        // 优先级,排序使用
	children  []*node       // 子节点 child nodes, at most 1 :param style node at the end of the array
	handlers  HandlersChain // 对应的处理函数
	fullPath  string        // 从根节点到达当前节点完整字符串
}

疑问点: indices是干啥的?

举个例子:比如有个节点a,他的子节点有个 boy girl man,会在节点aindices记录子节点的首字母(即:bgm),这样如果我们要查找节点a下面的子节点 woman,通过bgm这串字符串可知,里面没有w字母,都不用查看子节点了。



// 最长公共前缀长度
func longestCommonPrefix(a, b string) int {
	i := 0
	max := min(len(a), len(b))    // 两个字符串的最小的长度
	for i < max && a[i] == b[i] { // 从头部开始,比较字符串,找到相同的部分
		i++
	}
	return i // 表示相同部分的长度
}

// addRoute adds a node with the given handle to the path.
// Not concurrency-safe!

// 不是并发安全
func (n *node) addRoute(path string, handlers HandlersChain) {

	// path保存到fullPath中
	fullPath := path
	n.priority++

	// 【注释1】第一次执行addRoute函数,path 和 children 肯定都是没有值(即:这棵树是一个没有任何数据的空树)
	// Empty tree
	if len(n.path) == 0 && len(n.children) == 0 {

		// 只看没有通配符的情况,相当于直接将path/fullPath/handlers保存到 n *node中
		n.insertChild(path, fullPath, handlers)
		n.nType = root
		return
	}

	parentFullPathIndex := 0

walk:
	for {
		// Find the longest common prefix.
		// This also implies that the common prefix contains no ':' or '*'
		// since the existing key can't contain those chars.

		// 【注释2】 获取 path 和 n.path 的公共部分的长度
		i := longestCommonPrefix(path, n.path)

		// Split edge
		// 【注释3】(分裂节点) 比如 n.path = /a path = /b ,两个的公共前缀就只有 /,此时的n.path会分裂为 / 和 一个子节点 a
		if i < len(n.path) {
			// 相当于分裂节点n,将 n.path[i:]这部分分裂出来(即:a),形成了一个新的child节点
			child := node{
				path:      n.path[i:], // child节点分裂出来的部分(也就是/a 分裂出来的a)
				wildChild: n.wildChild,
				nType:     static,
				indices:   n.indices,  // 继承父的indices
				children:  n.children, // 继承父的所有的子节点
				handlers:  n.handlers,
				priority:  n.priority - 1,
				fullPath:  n.fullPath, // 继承父亲
			}

			// 分裂出来的child节点,成了当前节点n的子节点
			n.children = []*node{&child}
			// []byte for proper unicode char conversion, see #65
			n.indices = bytesconv.BytesToString([]byte{n.path[i]}) // 相当于将child节点中path的第一个字符,提取出来保存到,当前节点n的indices中(可以快速的判断当前节点n后面跟了哪些子节点)
			n.path = path[:i]                                      // 当前节点剩下的公共部分(即 /)
			n.handlers = nil                                       // 因为已经遗传给了child,自己就变成了nil
			n.wildChild = false
			n.fullPath = fullPath[:parentFullPathIndex+i] // 当前节点n表示的全路径(原来是 /a,现在是/)
		}

		// Make new node a child of this node

		//【注释4】
		// 当 n.path = /a , path = /b 上面的if条件先执行,形成了当前节点为/ 和子节点为a的情况
		// 执行到这里,i 也是小于 len(path),因为当前节点已经有了 / ,那么我们只需要将字符b当作和子节点a一样,也放到 / 后面不就形成了 b子节点
		if i < len(path) {
			path = path[i:] // 这里相当于截取出了 b,并更新path
			c := path[0]    // 同时将第一个字符也提取出来 ,就是字符b

			// '/' after param
			if n.nType == param && c == '/' && len(n.children) == 1 {
				parentFullPathIndex += len(n.path)
				n = n.children[0] // 相当于 n 的指向 向下移动了一下
				n.priority++
				continue walk
			}

			// Check if a child with the next path byte exists

			// 这里检查当前节点n后面,是否已经存在以 b字符开头的子节点
			for i, max := 0, len(n.indices); i < max; i++ {
				if c == n.indices[i] { // 找到了,以 b字符 开头的child节点
					parentFullPathIndex += len(n.path)
					i = n.incrementChildPrio(i) // 调整子节点的优先级
					n = n.children[i]           // 如果存在,那就让 n 指向这个 child节点
					continue walk               // 继续重复公共部分查找
				}
			}

			// Otherwise insert it
			// 执行到这里,说明 当前节点n后面没有以 b字符开始的子节点
			if c != ':' && c != '*' && n.nType != catchAll {
				// []byte for proper unicode char conversion, see #65
				n.indices += bytesconv.BytesToString([]byte{c}) // 将 b字符 拼接到 n.indices 中

				// 同时创建一个新的child节点
				child := &node{
					fullPath: fullPath,
				}
				n.addChild(child)                        // 将child节点放置到 当前n节点的n.children中
				n.incrementChildPrio(len(n.indices) - 1) // 相当于将频繁使用的子节点,调整位置
				n = child                                // 然后 n 指向 这个child节点
			} else if n.wildChild {
				// inserting a wildcard node, need to check if it conflicts with the existing wildcard
				n = n.children[len(n.children)-1]
				n.priority++

				// Check if the wildcard matches
				if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
					// Adding a child to a catchAll is not possible
					n.nType != catchAll &&
					// Check for longer wildcard, e.g. :name and :names
					(len(n.path) >= len(path) || path[len(n.path)] == '/') {
					continue walk
				}

				// Wildcard conflict
				pathSeg := path
				if n.nType != catchAll {
					pathSeg = strings.SplitN(pathSeg, "/", 2)[0]
				}
				prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
				panic("'" + pathSeg +
					"' in new path '" + fullPath +
					"' conflicts with existing wildcard '" + n.path +
					"' in existing prefix '" + prefix +
					"'")
			}

			// 修改 n 节点中的 path, fullPath, handlers 三个值(这里的n其实就上child)
			n.insertChild(path, fullPath, handlers)
			return
		}

		// 【注释5】执行到这里,说明 【公共前缀的长度】 和 【path 的长度相同】:例如 n.path = /a , path = / 经过上面的逻辑 n.path 分裂为 / 和  子节点a,此时n指向的/位置,就是我们要找到的位置
		// Otherwise add handle to current node
		if n.handlers != nil { // 前提,当前位置没有被重复注册
			panic("handlers are already registered for path '" + fullPath + "'")
		}
		n.handlers = handlers
		n.fullPath = fullPath
		return
	}
}

简单总结下:

  • 刚开始添加路径 /a 的时候,因为是第一次添加,直接保存到根节点中
  • 添加路径 /b 的时候,先看下n指向的根节点的 n.pathpath的公共前缀,目的在于判定n指向的节点需不需要进行拆分;如果 i < len(n.path),说明需要拆分;否则就保持n不变;继续判断 i 和 path的长度关系,如果 i < len(path),说明 path也需要拆分,将 path[i:] 剩下的字符串,作为子节点保存到 n 节点的后面
  • 添加路径 / 的时候,因为根节点的 n.path值和path值正好完全重叠,表示匹配到了最终的位置。
router.Group 函数

新创建一个 *RouterGroup对象,并且其中的 Handlers basePath的值是由 当前的 group *RouterGroup中的值叠加而来。(新对象属性是老对象属性的基础上叠加而来)

// 在现有 group的基础上,新创建一个*RouterGroup
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {

	// 创建了一个新的 *RouterGroup对象
	// 而且 新 *RouterGroup对象的Handlers/basePath值,叠加了当前的group *RouterGroup的现有的值
	return &RouterGroup{
		Handlers: group.combineHandlers(handlers),
		basePath: group.calculateAbsolutePath(relativePath),
		engine:   group.engine,
	}
}

router.Static 函数

指定静态文件的存储路径

router.Static("/static", ".")

代码含义:

  • 当用户访问 http://127.0.0.1:8000/static/test.txt服务端会提取出路由/static/test.txt,并且去掉/static/只保留 test.txt;
  • 然后去服务端 .目录下查找test.txt,并将内容回显给客户端。

底层实现:

  • Static函数内部其实在调用 StaticFS函数
  • StaticFS函数内部,调用 GET HEAD方法,在方法树中添加路由 + 处理函数的映射
  • 比较复杂的是 createStaticHandler函数,代码逻辑套了很多层次,按照我的代码注释一层层的看进去可知,最后调用fs := http.Dir(root) 中的Open方法打开文件句柄,最后在 serveContent函数中执行 io.CopyN(w, sendContent, sendSize)发送数据给客户端。

(为了便于理解下面有图片,讲解流程)


func (group *RouterGroup) Static(relativePath, root string) IRoutes {
	return group.StaticFS(relativePath, Dir(root, false))
}

func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
		panic("URL parameters can not be used when serving a static folder")
	}

	// 路由对应的处理函数
	handler := group.createStaticHandler(relativePath, fs)
	// 路由
	urlPattern := path.Join(relativePath, "/*filepath")

	// Register GET and HEAD handlers
	group.GET(urlPattern, handler) // 将路由 + 对应的处理函数 保存到方法树中
	group.HEAD(urlPattern, handler)
	return group.returnObj()
}


func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
	absolutePath := group.calculateAbsolutePath(relativePath)
	// StripPrefix 会去掉 r.URL.Path 中的前缀 absolutePath,后续从 r.URL.Path 获取文件路径的时候,就不会有前缀
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
	return func(c *Context) {
		if _, noListing := fs.(*onlyFilesFS); noListing {
			c.Writer.WriteHeader(http.StatusNotFound)
		}

		// 从 url 中提取出 文件路径
		file := c.Param("filepath")
		// Check if file exists and/or if we have permission to access it
		f, err := fs.Open(file) // 判断文件是否存在(文件路径:就是在 StaticFS 中设定的root string + 从url取出的路径,即为在服务器中的文件路径)
		if err != nil {
			c.Writer.WriteHeader(http.StatusNotFound)
			c.handlers = group.engine.noRoute
			// Reset index
			c.index = -1
			return
		}
		f.Close()
		// fileServer.ServeHTTP 函数,实际执行的是 http.FileServer(fs).ServeHTTP 函数,
		// 而在http.FileServer(fs).ServeHTTP 内部,会执行serveFile函数
		// 再看 serveFile函数内部,实际执行的 fs.Open(name),而 fs 就是 Dir(root, false)
		// 所以直接看 Dir(root, false), 最终执行的其实是 fs := http.Dir(root) 中的Open方法
		fileServer.ServeHTTP(c.Writer, c.Request)
	}
}

最后我们在 serveFile函数中代码拉到最后,看到有一行 serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f) 函数,这里就是将 f句柄对应的文件发送出去,进入serveContent函数再拉去到函数底部io.CopyN(w, sendContent, sendSize)就是真正的发送。

Routes 函数

就是多叉树的前序遍历;

遍历的过程中判断节点中的root.handlers是否 > 0,说明节点是一个有效的路由的尾部节点

func (engine *Engine) Routes() (routes RoutesInfo) {
	for _, tree := range engine.trees { // 遍历所有的方法树
		routes = iterate("", tree.method, routes, tree.root) // 针对每一个方法树,使用深度递归的方式,从根节点开始,查找每棵树中的路径
	}
	return routes
}

// 递归的遍历整个多叉树(前序遍历)
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
	// 累计拼接路径
	path += root.path
	// 判断是否有响应函数。 有,说明当前节点是一个有效的路由值
	if len(root.handlers) > 0 {
		handlerFunc := root.handlers.Last()
		routes = append(routes, RouteInfo{
			Method:      method,
			Path:        path,
			Handler:     nameOfFunction(handlerFunc),
			HandlerFunc: handlerFunc,
		})
	}
	// 继续查看 当前的节点的所有的子节点
	for _, child := range root.children {
		routes = iterate(path, method, routes, child)
	}
	return routes
}
Run 函数

这个Run函数执行后,服务端正式启动,并监听在8080端口等待请求到来

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/gofish2020/gin#readme-don-t-trust-all-proxies for details.")
	}

	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine.Handler()) // 【注意】第二个参数一般情况下为nil,这里相当使用我们自己的Hander
	return
}

一旦请求到来后,所有的请求都会执行 engine.ServeHTTP函数(这里涉及 net/http库的底层实现原理,待补充)

// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {

	// 从对象池中获取一个 *Context
	c := engine.pool.Get().(*Context)

	// 将 w 和req 保存到 *Context中
	c.writermem.reset(w)
	c.Request = req
	c.reset()

	// 基于请求的信息,在方法树中,查询出路由对应的响应函数,并且执行
	engine.handleHTTPRequest(c)

	// 用完放回池子
	engine.pool.Put(c)
}

  • c.Request中提取出 请求方法 + 路由;
  • root.getValue函数中查找对应的响应函数,并保存到 上下文*Context
  • 调用 c.Next()函数,顺序执行上下文中的所有的响应函数
func (engine *Engine) handleHTTPRequest(c *Context) {
	// 请求方法
	httpMethod := c.Request.Method
	// 请求路由
	rPath := c.Request.URL.Path
	unescape := false
	if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
		rPath = c.Request.URL.RawPath
		unescape = engine.UnescapePathValues
	}

	if engine.RemoveExtraSlash { // 去掉多余的 /
		rPath = cleanPath(rPath)
	}

	// Find root of the tree for the given HTTP method
	t := engine.trees // 方法树
	for i, tl := 0, len(t); i < tl; i++ {
		if t[i].method != httpMethod {
			continue
		}

		// 执行到这里,说明找到了当前请求方法对应的方法树
		root := t[i].root
		// Find route in tree
		value := root.getValue(rPath, c.params, c.skippedNodes, unescape) // 从方法树中找到路由对应的 处理函数

		// 将 value中的值,都保存到 *Context中
		if value.params != nil {
			c.Params = *value.params
		}
		if value.handlers != nil {
			c.handlers = value.handlers
			c.fullPath = value.fullPath
			c.Next() // 执行方法(顺序执行 c.handlers中的所有的函数方法)
			c.writermem.WriteHeaderNow()
			return
		}
		if httpMethod != http.MethodConnect && rPath != "/" {
			if value.tsr && engine.RedirectTrailingSlash {
				redirectTrailingSlash(c)
				return
			}
			if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
				return
			}
		}
		break
	}

	// 当HandleMethodNotAllowed = true 表示:当前的 httpMethod 方法不存在,看下是否允许其他的请求方法,并将结果通过 Allow告知前端
	if engine.HandleMethodNotAllowed {
		// According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
		// containing a list of the target resource's currently supported methods.
		allowed := make([]string, 0, len(t)-1)
		for _, tree := range engine.trees {
			if tree.method == httpMethod {
				continue
			}
			if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
				allowed = append(allowed, tree.method)
			}
		}
		if len(allowed) > 0 {
			c.handlers = engine.allNoMethod
			c.writermem.Header().Set("Allow", strings.Join(allowed, ", "))
			serveError(c, http.StatusMethodNotAllowed, default405Body)
			return
		}
	}

	// 如果上面的没有对应的路由,直接返回404
	c.handlers = engine.allNoRoute
	serveError(c, http.StatusNotFound, default404Body)
}

这里截取一部分的getValue函数逻辑,介绍下是怎么匹配字符串的。

就是用 path 和 每个节点的 n.path 进行对比,将 n.pathpath 相同的前缀部分去掉,然后剩下 path[len(prefix):]继续和子节点 n.path进行匹配,直到剩下path和某个节点的 n.path 完全相同,说明找到了,把里面的响应函数提取出来,保存到 nodeValue中返回。

func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) {
	var globalParamsCount int16

walk: // Outer loop for walking the tree
	for {
		prefix := n.path             // 当前 节点n中的n.path的值
		if len(path) > len(prefix) { // 如果 要查找的 path > n.path, 说明需要继续向下查找
			if path[:len(prefix)] == prefix {
				path = path[len(prefix):] // 去掉path中的前缀,剩下的字符串,继续从当前n节点,向子节点查找

				// Try all the non-wildcard children first by matching the indices
				idxc := path[0]
				for i, c := range []byte(n.indices) { // 查找 当前节点n的所有的子节点,(是否有 idxc开头的子节点)
					if c == idxc { // 如果找到了,子节点
						//  strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
						if n.wildChild {
							index := len(*skippedNodes)
							*skippedNodes = (*skippedNodes)[:index+1]
							(*skippedNodes)[index] = skippedNode{
								path: prefix + path,
								node: &node{
									path:      n.path,
									wildChild: n.wildChild,
									nType:     n.nType,
									priority:  n.priority,
									children:  n.children,
									handlers:  n.handlers,
									fullPath:  n.fullPath,
								},
								paramsCount: globalParamsCount,
							}
						}

						n = n.children[i] // 让n指向 子节点,继续匹配剩下的path
						continue walk
					}
				}
		}

		// 说明当前的节点的n.path的值和 要找的path相同
		if path == prefix {
			// If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node
			// the current node needs to roll back to last valid skippedNode
			if n.handlers == nil && path != "/" {
				for length := len(*skippedNodes); length > 0; length-- {
					skippedNode := (*skippedNodes)[length-1]
					*skippedNodes = (*skippedNodes)[:length-1]
					if strings.HasSuffix(skippedNode.path, path) {
						path = skippedNode.path
						n = skippedNode.node
						if value.params != nil {
							*value.params = (*value.params)[:skippedNode.paramsCount]
						}
						globalParamsCount = skippedNode.paramsCount
						continue walk
					}
				}
				//	n = latestNode.children[len(latestNode.children)-1]
			}
			// We should have reached the node containing the handle.
			// Check if this node has a handle registered.
			if value.handlers = n.handlers; value.handlers != nil { // 并且 n节点中的handlers中是有注册处理函数的
				value.fullPath = n.fullPath
				return value
			}

			
		}
	}
}

待补充

当服务器调用Run启动以后,怎么就执行到 engine.ServeHTTP函数了呢。这块是关于net/http网络库的底层实现,我会再写一篇文件介绍。

Documentation

Overview

Package gin implements a HTTP web framework called gin.

See https://gin-gonic.com/ for more information about gin.

Index

Constants

View Source
const (
	MIMEJSON              = binding.MIMEJSON
	MIMEHTML              = binding.MIMEHTML
	MIMEXML               = binding.MIMEXML
	MIMEXML2              = binding.MIMEXML2
	MIMEPlain             = binding.MIMEPlain
	MIMEPOSTForm          = binding.MIMEPOSTForm
	MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
	MIMEYAML              = binding.MIMEYAML
	MIMETOML              = binding.MIMETOML
)

Content-Type MIME of the most common data formats.

View Source
const (
	// PlatformGoogleAppEngine when running on Google App Engine. Trust X-Appengine-Remote-Addr
	// for determining the client's IP
	PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
	// PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining
	// the client's IP
	PlatformCloudflare = "CF-Connecting-IP"
	// PlatformFlyIO when running on Fly.io. Trust Fly-Client-IP for determining the client's IP
	PlatformFlyIO = "Fly-Client-IP"
)

Trusted platforms

View Source
const (
	// DebugMode indicates gin mode is debug.
	DebugMode = "debug"
	// ReleaseMode indicates gin mode is release.
	ReleaseMode = "release"
	// TestMode indicates gin mode is test.
	TestMode = "test"
)
View Source
const AuthUserKey = "user"

AuthUserKey is the cookie name for user credential in basic auth.

View Source
const BindKey = "_gin-gonic/gin/bindkey"

BindKey indicates a default bind key.

View Source
const BodyBytesKey = "_gin-gonic/gin/bodybyteskey"

BodyBytesKey indicates a default body bytes key.

View Source
const ContextKey = "_gin-gonic/gin/contextkey"

ContextKey is the key that a Context returns itself for.

View Source
const EnvGinMode = "GIN_MODE"

EnvGinMode indicates environment name for gin mode.

View Source
const Version = "v1.9.1"

Version is the current gin framework's version.

Variables

View Source
var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)

DebugPrintRouteFunc indicates debug log output format.

View Source
var DefaultErrorWriter io.Writer = os.Stderr

DefaultErrorWriter is the default io.Writer used by Gin to debug errors

View Source
var DefaultWriter io.Writer = os.Stdout

DefaultWriter is the default io.Writer used by Gin for debug output and middleware output like Logger() or Recovery(). Note that both Logger and Recovery provides custom ways to configure their output io.Writer. To support coloring in Windows use:

import "github.com/mattn/go-colorable"
gin.DefaultWriter = colorable.NewColorableStdout()

Functions

func CreateTestContext

func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine)

CreateTestContext returns a fresh engine and context for testing purposes

func Dir

func Dir(root string, listDirectory bool) http.FileSystem

Dir returns a http.FileSystem that can be used by http.FileServer(). It is used internally in router.Static(). if listDirectory == true, then it works the same as http.Dir() otherwise it returns a filesystem that prevents http.FileServer() to list the directory files.

func DisableBindValidation

func DisableBindValidation()

DisableBindValidation closes the default validator.

func DisableConsoleColor

func DisableConsoleColor()

DisableConsoleColor disables color output in the console.

func EnableJsonDecoderDisallowUnknownFields

func EnableJsonDecoderDisallowUnknownFields()

EnableJsonDecoderDisallowUnknownFields sets true for binding.EnableDecoderDisallowUnknownFields to call the DisallowUnknownFields method on the JSON Decoder instance.

func EnableJsonDecoderUseNumber

func EnableJsonDecoderUseNumber()

EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumber to call the UseNumber method on the JSON Decoder instance.

func ForceConsoleColor

func ForceConsoleColor()

ForceConsoleColor force color output in the console.

func IsDebugging

func IsDebugging() bool

IsDebugging returns true if the framework is running in debug mode. Use SetMode(gin.ReleaseMode) to disable debug mode.

func Mode

func Mode() string

Mode returns current gin mode.

func SetMode

func SetMode(value string)

SetMode sets gin mode according to input string.

Types

type Accounts

type Accounts map[string]string

Accounts defines a key/value for user/pass list of authorized logins.

type Context

type Context struct {
	Request *http.Request
	Writer  ResponseWriter

	Params Params

	// Keys is a key/value pair exclusively for the context of each request.
	Keys map[string]any

	// Errors is a list of errors attached to all the handlers/middlewares who used this context.
	Errors errorMsgs

	// Accepted defines a list of manually accepted formats for content negotiation.
	Accepted []string
	// contains filtered or unexported fields
}

Context is the most important part of gin. It allows us to pass variables between middleware, manage the flow, validate the JSON of a request and render a JSON response for example.

func CreateTestContextOnly

func CreateTestContextOnly(w http.ResponseWriter, r *Engine) (c *Context)

CreateTestContextOnly returns a fresh context base on the engine for testing purposes

func (*Context) Abort

func (c *Context) Abort()

Abort prevents pending handlers from being called. Note that this will not stop the current handler. Let's say you have an authorization middleware that validates that the current request is authorized. If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers for this request are not called.

func (*Context) AbortWithError

func (c *Context) AbortWithError(code int, err error) *Error

AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and pushes the specified error to `c.Errors`. See Context.Error() for more details.

func (*Context) AbortWithStatus

func (c *Context) AbortWithStatus(code int)

AbortWithStatus calls `Abort()` and writes the headers with the specified status code. For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401).

func (*Context) AbortWithStatusJSON

func (c *Context) AbortWithStatusJSON(code int, jsonObj any)

AbortWithStatusJSON calls `Abort()` and then `JSON` internally. This method stops the chain, writes the status code and return a JSON body. It also sets the Content-Type as "application/json".

func (*Context) AddParam

func (c *Context) AddParam(key, value string)

AddParam adds param to context and replaces path param key with given value for e2e testing purposes Example Route: "/user/:id" AddParam("id", 1) Result: "/user/1"

func (*Context) AsciiJSON

func (c *Context) AsciiJSON(code int, obj any)

AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. It also sets the Content-Type as "application/json".

func (*Context) Bind

func (c *Context) Bind(obj any) error

Bind checks the Method and Content-Type to select a binding engine automatically, Depending on the "Content-Type" header different bindings are used, for example:

"application/json" --> JSON binding
"application/xml"  --> XML binding

It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer. It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.

func (*Context) BindHeader

func (c *Context) BindHeader(obj any) error

BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).

func (*Context) BindJSON

func (c *Context) BindJSON(obj any) error

BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).

func (*Context) BindQuery

func (c *Context) BindQuery(obj any) error

BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).

func (*Context) BindTOML

func (c *Context) BindTOML(obj any) error

BindTOML is a shortcut for c.MustBindWith(obj, binding.TOML).

func (*Context) BindUri

func (c *Context) BindUri(obj any) error

BindUri binds the passed struct pointer using binding.Uri. It will abort the request with HTTP 400 if any error occurs.

func (*Context) BindWith

func (c *Context) BindWith(obj any, b binding.Binding) error

BindWith binds the passed struct pointer using the specified binding engine. See the binding package.

func (*Context) BindXML

func (c *Context) BindXML(obj any) error

BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).

func (*Context) BindYAML

func (c *Context) BindYAML(obj any) error

BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).

func (*Context) ClientIP

func (c *Context) ClientIP() string

ClientIP implements one best effort algorithm to return the real client IP. It calls c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not. If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]). If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy, the remote IP (coming from Request.RemoteAddr) is returned.

func (*Context) ContentType

func (c *Context) ContentType() string

ContentType returns the Content-Type header of the request.

func (*Context) Cookie

func (c *Context) Cookie(name string) (string, error)

Cookie returns the named cookie provided in the request or ErrNoCookie if not found. And return the named cookie is unescaped. If multiple cookies match the given name, only one cookie will be returned.

func (*Context) Copy

func (c *Context) Copy() *Context

Copy returns a copy of the current context that can be safely used outside the request's scope. This has to be used when the context has to be passed to a goroutine.

func (*Context) Data

func (c *Context) Data(code int, contentType string, data []byte)

Data writes some data into the body stream and updates the HTTP code.

func (*Context) DataFromReader

func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)

DataFromReader writes the specified reader into the body stream and updates the HTTP code.

func (*Context) Deadline

func (c *Context) Deadline() (deadline time.Time, ok bool)

Deadline returns that there is no deadline (ok==false) when c.Request has no Context.

func (*Context) DefaultPostForm

func (c *Context) DefaultPostForm(key, defaultValue string) string

DefaultPostForm returns the specified key from a POST urlencoded form or multipart form when it exists, otherwise it returns the specified defaultValue string. See: PostForm() and GetPostForm() for further information.

func (*Context) DefaultQuery

func (c *Context) DefaultQuery(key, defaultValue string) string

DefaultQuery returns the keyed url query value if it exists, otherwise it returns the specified defaultValue string. See: Query() and GetQuery() for further information.

GET /?name=Manu&lastname=
c.DefaultQuery("name", "unknown") == "Manu"
c.DefaultQuery("id", "none") == "none"
c.DefaultQuery("lastname", "none") == ""

func (*Context) Done

func (c *Context) Done() <-chan struct{}

Done returns nil (chan which will wait forever) when c.Request has no Context.

func (*Context) Err

func (c *Context) Err() error

Err returns nil when c.Request has no Context.

func (*Context) Error

func (c *Context) Error(err error) *Error

Error attaches an error to the current context. The error is pushed to a list of errors. It's a good idea to call Error for each error that occurred during the resolution of a request. A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response. Error will panic if err is nil.

func (*Context) File

func (c *Context) File(filepath string)

File writes the specified file into the body stream in an efficient way.

func (*Context) FileAttachment

func (c *Context) FileAttachment(filepath, filename string)

FileAttachment writes the specified file into the body stream in an efficient way On the client side, the file will typically be downloaded with the given filename

func (*Context) FileFromFS

func (c *Context) FileFromFS(filepath string, fs http.FileSystem)

FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way.

func (*Context) FormFile

func (c *Context) FormFile(name string) (*multipart.FileHeader, error)

FormFile returns the first file for the provided form key.

func (*Context) FullPath

func (c *Context) FullPath() string

FullPath returns a matched route full path. For not found routes returns an empty string.

router.GET("/user/:id", func(c *gin.Context) {
    c.FullPath() == "/user/:id" // true
})

func (*Context) Get

func (c *Context) Get(key string) (value any, exists bool)

Get returns the value for the given key, ie: (value, true). If the value does not exist it returns (nil, false)

func (*Context) GetBool

func (c *Context) GetBool(key string) (b bool)

GetBool returns the value associated with the key as a boolean.

func (*Context) GetDuration

func (c *Context) GetDuration(key string) (d time.Duration)

GetDuration returns the value associated with the key as a duration.

func (*Context) GetFloat64

func (c *Context) GetFloat64(key string) (f64 float64)

GetFloat64 returns the value associated with the key as a float64.

func (*Context) GetHeader

func (c *Context) GetHeader(key string) string

GetHeader returns value from request headers.

func (*Context) GetInt

func (c *Context) GetInt(key string) (i int)

GetInt returns the value associated with the key as an integer.

func (*Context) GetInt64

func (c *Context) GetInt64(key string) (i64 int64)

GetInt64 returns the value associated with the key as an integer.

func (*Context) GetPostForm

func (c *Context) GetPostForm(key string) (string, bool)

GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded form or multipart form when it exists `(value, true)` (even when the value is an empty string), otherwise it returns ("", false). For example, during a PATCH request to update the user's email:

    email=mail@example.com  -->  ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
	   email=                  -->  ("", true) := GetPostForm("email") // set email to ""
                            -->  ("", false) := GetPostForm("email") // do nothing with email

func (*Context) GetPostFormArray

func (c *Context) GetPostFormArray(key string) (values []string, ok bool)

GetPostFormArray returns a slice of strings for a given form key, plus a boolean value whether at least one value exists for the given key.

func (*Context) GetPostFormMap

func (c *Context) GetPostFormMap(key string) (map[string]string, bool)

GetPostFormMap returns a map for a given form key, plus a boolean value whether at least one value exists for the given key.

func (*Context) GetQuery

func (c *Context) GetQuery(key string) (string, bool)

GetQuery is like Query(), it returns the keyed url query value if it exists `(value, true)` (even when the value is an empty string), otherwise it returns `("", false)`. It is shortcut for `c.Request.URL.Query().Get(key)`

GET /?name=Manu&lastname=
("Manu", true) == c.GetQuery("name")
("", false) == c.GetQuery("id")
("", true) == c.GetQuery("lastname")

func (*Context) GetQueryArray

func (c *Context) GetQueryArray(key string) (values []string, ok bool)

GetQueryArray returns a slice of strings for a given query key, plus a boolean value whether at least one value exists for the given key.

func (*Context) GetQueryMap

func (c *Context) GetQueryMap(key string) (map[string]string, bool)

GetQueryMap returns a map for a given query key, plus a boolean value whether at least one value exists for the given key.

func (*Context) GetRawData

func (c *Context) GetRawData() ([]byte, error)

GetRawData returns stream data.

func (*Context) GetString

func (c *Context) GetString(key string) (s string)

GetString returns the value associated with the key as a string.

func (*Context) GetStringMap

func (c *Context) GetStringMap(key string) (sm map[string]any)

GetStringMap returns the value associated with the key as a map of interfaces.

func (*Context) GetStringMapString

func (c *Context) GetStringMapString(key string) (sms map[string]string)

GetStringMapString returns the value associated with the key as a map of strings.

func (*Context) GetStringMapStringSlice

func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)

GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.

func (*Context) GetStringSlice

func (c *Context) GetStringSlice(key string) (ss []string)

GetStringSlice returns the value associated with the key as a slice of strings.

func (*Context) GetTime

func (c *Context) GetTime(key string) (t time.Time)

GetTime returns the value associated with the key as time.

func (*Context) GetUint

func (c *Context) GetUint(key string) (ui uint)

GetUint returns the value associated with the key as an unsigned integer.

func (*Context) GetUint64

func (c *Context) GetUint64(key string) (ui64 uint64)

GetUint64 returns the value associated with the key as an unsigned integer.

func (*Context) HTML

func (c *Context) HTML(code int, name string, obj any)

HTML renders the HTTP template specified by its file name. It also updates the HTTP code and sets the Content-Type as "text/html". See http://golang.org/doc/articles/wiki/

func (*Context) Handler

func (c *Context) Handler() HandlerFunc

Handler returns the main handler.

func (*Context) HandlerName

func (c *Context) HandlerName() string

HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this function will return "main.handleGetUsers".

func (*Context) HandlerNames

func (c *Context) HandlerNames() []string

HandlerNames returns a list of all registered handlers for this context in descending order, following the semantics of HandlerName()

func (*Context) Header

func (c *Context) Header(key, value string)

Header is an intelligent shortcut for c.Writer.Header().Set(key, value). It writes a header in the response. If value == "", this method removes the header `c.Writer.Header().Del(key)`

func (*Context) IndentedJSON

func (c *Context) IndentedJSON(code int, obj any)

IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body. It also sets the Content-Type as "application/json". WARNING: we recommend using this only for development purposes since printing pretty JSON is more CPU and bandwidth consuming. Use Context.JSON() instead.

func (*Context) IsAborted

func (c *Context) IsAborted() bool

IsAborted returns true if the current context was aborted.

func (*Context) IsWebsocket

func (c *Context) IsWebsocket() bool

IsWebsocket returns true if the request headers indicate that a websocket handshake is being initiated by the client.

func (*Context) JSON

func (c *Context) JSON(code int, obj any)

JSON serializes the given struct as JSON into the response body. It also sets the Content-Type as "application/json".

func (*Context) JSONP

func (c *Context) JSONP(code int, obj any)

JSONP serializes the given struct as JSON into the response body. It adds padding to response body to request data from a server residing in a different domain than the client. It also sets the Content-Type as "application/javascript".

func (*Context) MultipartForm

func (c *Context) MultipartForm() (*multipart.Form, error)

MultipartForm is the parsed multipart form, including file uploads.

func (*Context) MustBindWith

func (c *Context) MustBindWith(obj any, b binding.Binding) error

MustBindWith binds the passed struct pointer using the specified binding engine. It will abort the request with HTTP 400 if any error occurs. See the binding package.

func (*Context) MustGet

func (c *Context) MustGet(key string) any

MustGet returns the value for the given key if it exists, otherwise it panics.

func (*Context) Negotiate

func (c *Context) Negotiate(code int, config Negotiate)

Negotiate calls different Render according to acceptable Accept format.

func (*Context) NegotiateFormat

func (c *Context) NegotiateFormat(offered ...string) string

NegotiateFormat returns an acceptable Accept format.

func (*Context) Next

func (c *Context) Next()

Next should be used only inside middleware. It executes the pending handlers in the chain inside the calling handler. See example in GitHub.

func (*Context) Param

func (c *Context) Param(key string) string

Param returns the value of the URL param. It is a shortcut for c.Params.ByName(key)

router.GET("/user/:id", func(c *gin.Context) {
    // a GET request to /user/john
    id := c.Param("id") // id == "/john"
    // a GET request to /user/john/
    id := c.Param("id") // id == "/john/"
})

func (*Context) PostForm

func (c *Context) PostForm(key string) (value string)

PostForm returns the specified key from a POST urlencoded form or multipart form when it exists, otherwise it returns an empty string `("")`.

func (*Context) PostFormArray

func (c *Context) PostFormArray(key string) (values []string)

PostFormArray returns a slice of strings for a given form key. The length of the slice depends on the number of params with the given key.

func (*Context) PostFormMap

func (c *Context) PostFormMap(key string) (dicts map[string]string)

PostFormMap returns a map for a given form key.

func (*Context) ProtoBuf

func (c *Context) ProtoBuf(code int, obj any)

ProtoBuf serializes the given struct as ProtoBuf into the response body.

func (*Context) PureJSON

func (c *Context) PureJSON(code int, obj any)

PureJSON serializes the given struct as JSON into the response body. PureJSON, unlike JSON, does not replace special html characters with their unicode entities.

func (*Context) Query

func (c *Context) Query(key string) (value string)

Query returns the keyed url query value if it exists, otherwise it returns an empty string `("")`. It is shortcut for `c.Request.URL.Query().Get(key)`

    GET /path?id=1234&name=Manu&value=
	   c.Query("id") == "1234"
	   c.Query("name") == "Manu"
	   c.Query("value") == ""
	   c.Query("wtf") == ""

func (*Context) QueryArray

func (c *Context) QueryArray(key string) (values []string)

QueryArray returns a slice of strings for a given query key. The length of the slice depends on the number of params with the given key.

func (*Context) QueryMap

func (c *Context) QueryMap(key string) (dicts map[string]string)

QueryMap returns a map for a given query key.

func (*Context) Redirect

func (c *Context) Redirect(code int, location string)

Redirect returns an HTTP redirect to the specific location.

func (*Context) RemoteIP

func (c *Context) RemoteIP() string

RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).

func (*Context) Render

func (c *Context) Render(code int, r render.Render)

Render writes the response headers and calls render.Render to render data.

func (*Context) SSEvent

func (c *Context) SSEvent(name string, message any)

SSEvent writes a Server-Sent Event into the body stream.

func (*Context) SaveUploadedFile

func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error

SaveUploadedFile uploads the form file to specific dst.

func (*Context) SecureJSON

func (c *Context) SecureJSON(code int, obj any)

SecureJSON serializes the given struct as Secure JSON into the response body. Default prepends "while(1)," to response body if the given struct is array values. It also sets the Content-Type as "application/json".

func (*Context) Set

func (c *Context) Set(key string, value any)

Set is used to store a new key/value pair exclusively for this context. It also lazy initializes c.Keys if it was not used previously.

func (*Context) SetAccepted

func (c *Context) SetAccepted(formats ...string)

SetAccepted sets Accept header data.

func (*Context) SetCookie

func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)

SetCookie adds a Set-Cookie header to the ResponseWriter's headers. The provided cookie must have a valid Name. Invalid cookies may be silently dropped.

func (*Context) SetSameSite

func (c *Context) SetSameSite(samesite http.SameSite)

SetSameSite with cookie

func (*Context) ShouldBind

func (c *Context) ShouldBind(obj any) error

ShouldBind checks the Method and Content-Type to select a binding engine automatically, Depending on the "Content-Type" header different bindings are used, for example:

"application/json" --> JSON binding
"application/xml"  --> XML binding

It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer. Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid.

func (*Context) ShouldBindBodyWith

func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error)

ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request body into the context, and reuse when it is called again.

NOTE: This method reads the body before binding. So you should use ShouldBindWith for better performance if you need to call only once.

func (*Context) ShouldBindHeader

func (c *Context) ShouldBindHeader(obj any) error

ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).

func (*Context) ShouldBindJSON

func (c *Context) ShouldBindJSON(obj any) error

ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).

func (*Context) ShouldBindQuery

func (c *Context) ShouldBindQuery(obj any) error

ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).

func (*Context) ShouldBindTOML

func (c *Context) ShouldBindTOML(obj any) error

ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML).

func (*Context) ShouldBindUri

func (c *Context) ShouldBindUri(obj any) error

ShouldBindUri binds the passed struct pointer using the specified binding engine.

func (*Context) ShouldBindWith

func (c *Context) ShouldBindWith(obj any, b binding.Binding) error

ShouldBindWith binds the passed struct pointer using the specified binding engine. See the binding package.

func (*Context) ShouldBindXML

func (c *Context) ShouldBindXML(obj any) error

ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).

func (*Context) ShouldBindYAML

func (c *Context) ShouldBindYAML(obj any) error

ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).

func (*Context) Status

func (c *Context) Status(code int)

Status sets the HTTP response code.

func (*Context) Stream

func (c *Context) Stream(step func(w io.Writer) bool) bool

Stream sends a streaming response and returns a boolean indicates "Is client disconnected in middle of stream"

func (*Context) String

func (c *Context) String(code int, format string, values ...any)

String writes the given string into the response body.

func (*Context) TOML

func (c *Context) TOML(code int, obj any)

TOML serializes the given struct as TOML into the response body.

func (*Context) Value

func (c *Context) Value(key any) any

Value returns the value associated with this context for key, or nil if no value is associated with key. Successive calls to Value with the same key returns the same result.

func (*Context) XML

func (c *Context) XML(code int, obj any)

XML serializes the given struct as XML into the response body. It also sets the Content-Type as "application/xml".

func (*Context) YAML

func (c *Context) YAML(code int, obj any)

YAML serializes the given struct as YAML into the response body.

type Engine

type Engine struct {
	RouterGroup

	// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
	// handler for the path with (without) the trailing slash exists.
	// For example if /foo/ is requested but a route only exists for /foo, the
	// client is redirected to /foo with http status code 301 for GET requests
	// and 307 for all other request methods.
	RedirectTrailingSlash bool

	// RedirectFixedPath if enabled, the router tries to fix the current request path, if no
	// handle is registered for it.
	// First superfluous path elements like ../ or // are removed.
	// Afterwards the router does a case-insensitive lookup of the cleaned path.
	// If a handle can be found for this route, the router makes a redirection
	// to the corrected path with status code 301 for GET requests and 307 for
	// all other request methods.
	// For example /FOO and /..//Foo could be redirected to /foo.
	// RedirectTrailingSlash is independent of this option.
	RedirectFixedPath bool

	// HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the
	// current route, if the current request can not be routed.
	// If this is the case, the request is answered with 'Method Not Allowed'
	// and HTTP status code 405.
	// If no other Method is allowed, the request is delegated to the NotFound
	// handler.
	HandleMethodNotAllowed bool

	// ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that
	// match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
	// fetched, it falls back to the IP obtained from
	// `(*gin.Context).Request.RemoteAddr`.
	ForwardedByClientIP bool

	// AppEngine was deprecated.
	// Deprecated: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
	// #726 #755 If enabled, it will trust some headers starting with
	// 'X-AppEngine...' for better integration with that PaaS.
	AppEngine bool

	// UseRawPath if enabled, the url.RawPath will be used to find parameters.
	UseRawPath bool

	// UnescapePathValues if true, the path value will be unescaped.
	// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
	// as url.Path gonna be used, which is already unescaped.
	UnescapePathValues bool

	// RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
	// See the PR #1817 and issue #1644
	RemoveExtraSlash bool

	// RemoteIPHeaders list of headers used to obtain the client IP when
	// `(*gin.Engine).ForwardedByClientIP` is `true` and
	// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
	// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
	RemoteIPHeaders []string

	// TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by
	// that platform, for example to determine the client IP
	TrustedPlatform string

	// MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
	// method call.
	MaxMultipartMemory int64

	// UseH2C enable h2c support.
	UseH2C bool

	// ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.
	ContextWithFallback bool

	HTMLRender render.HTMLRender
	FuncMap    template.FuncMap
	// contains filtered or unexported fields
}

Engine is the framework's instance, it contains the muxer, middleware and configuration settings. Create an instance of Engine, by using New() or Default()

func Default

func Default() *Engine

Default returns an Engine instance with the Logger and Recovery middleware already attached.

func New

func New() *Engine

New returns a new blank Engine instance without any middleware attached. By default, the configuration is: - RedirectTrailingSlash: true - RedirectFixedPath: false - HandleMethodNotAllowed: false - ForwardedByClientIP: true - UseRawPath: false - UnescapePathValues: true

func (*Engine) Delims

func (engine *Engine) Delims(left, right string) *Engine

Delims sets template left and right delims and returns an Engine instance.

func (*Engine) HandleContext

func (engine *Engine) HandleContext(c *Context)

HandleContext re-enters a context that has been rewritten. This can be done by setting c.Request.URL.Path to your new target. Disclaimer: You can loop yourself to deal with this, use wisely.

func (*Engine) Handler

func (engine *Engine) Handler() http.Handler

func (*Engine) LoadHTMLFiles

func (engine *Engine) LoadHTMLFiles(files ...string)

LoadHTMLFiles loads a slice of HTML files and associates the result with HTML renderer.

func (*Engine) LoadHTMLGlob

func (engine *Engine) LoadHTMLGlob(pattern string)

LoadHTMLGlob loads HTML files identified by glob pattern and associates the result with HTML renderer.

func (*Engine) NoMethod

func (engine *Engine) NoMethod(handlers ...HandlerFunc)

NoMethod sets the handlers called when Engine.HandleMethodNotAllowed = true.

func (*Engine) NoRoute

func (engine *Engine) NoRoute(handlers ...HandlerFunc)

NoRoute adds handlers for NoRoute. It returns a 404 code by default.

func (*Engine) Routes

func (engine *Engine) Routes() (routes RoutesInfo)

Routes returns a slice of registered routes, including some useful information, such as: the http method, path and the handler name.

func (*Engine) Run

func (engine *Engine) Run(addr ...string) (err error)

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) RunFd

func (engine *Engine) RunFd(fd int) (err error)

RunFd attaches the router to a http.Server and starts listening and serving HTTP requests through the specified file descriptor. Note: this method will block the calling goroutine indefinitely unless an error happens.

func (*Engine) RunListener

func (engine *Engine) RunListener(listener net.Listener) (err error)

RunListener attaches the router to a http.Server and starts listening and serving HTTP requests through the specified net.Listener

func (*Engine) RunTLS

func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error)

RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests. It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) Note: this method will block the calling goroutine indefinitely unless an error happens.

func (*Engine) RunUnix

func (engine *Engine) RunUnix(file string) (err error)

RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests through the specified unix socket (i.e. a file). Note: this method will block the calling goroutine indefinitely unless an error happens.

func (*Engine) SecureJsonPrefix

func (engine *Engine) SecureJsonPrefix(prefix string) *Engine

SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.

func (*Engine) ServeHTTP

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP conforms to the http.Handler interface.

func (*Engine) SetFuncMap

func (engine *Engine) SetFuncMap(funcMap template.FuncMap)

SetFuncMap sets the FuncMap used for template.FuncMap.

func (*Engine) SetHTMLTemplate

func (engine *Engine) SetHTMLTemplate(templ *template.Template)

SetHTMLTemplate associate a template with HTML renderer.

func (*Engine) SetTrustedProxies

func (engine *Engine) SetTrustedProxies(trustedProxies []string) error

SetTrustedProxies set a list of network origins (IPv4 addresses, IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs) from which to trust request's headers that contain alternative client IP when `(*gin.Engine).ForwardedByClientIP` is `true`. `TrustedProxies` feature is enabled by default, and it also trusts all proxies by default. If you want to disable this feature, use Engine.SetTrustedProxies(nil), then Context.ClientIP() will return the remote address directly.

func (*Engine) Use

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes

Use attaches a global middleware to the router. i.e. the middleware attached through Use() will be included in the handlers chain for every single request. Even 404, 405, static files... For example, this is the right place for a logger or error management middleware.

type Error

type Error struct {
	Err  error
	Type ErrorType
	Meta any
}

Error represents a error's specification.

func (Error) Error

func (msg Error) Error() string

Error implements the error interface.

func (*Error) IsType

func (msg *Error) IsType(flags ErrorType) bool

IsType judges one error.

func (*Error) JSON

func (msg *Error) JSON() any

JSON creates a properly formatted JSON

func (*Error) MarshalJSON

func (msg *Error) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaller interface.

func (*Error) SetMeta

func (msg *Error) SetMeta(data any) *Error

SetMeta sets the error's meta data.

func (*Error) SetType

func (msg *Error) SetType(flags ErrorType) *Error

SetType sets the error's type.

func (*Error) Unwrap

func (msg *Error) Unwrap() error

Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap()

type ErrorType

type ErrorType uint64

ErrorType is an unsigned 64-bit error code as defined in the gin spec.

const (
	// ErrorTypeBind is used when Context.Bind() fails.
	ErrorTypeBind ErrorType = 1 << 63
	// ErrorTypeRender is used when Context.Render() fails.
	ErrorTypeRender ErrorType = 1 << 62
	// ErrorTypePrivate indicates a private error.
	ErrorTypePrivate ErrorType = 1 << 0
	// ErrorTypePublic indicates a public error.
	ErrorTypePublic ErrorType = 1 << 1
	// ErrorTypeAny indicates any other error.
	ErrorTypeAny ErrorType = 1<<64 - 1
	// ErrorTypeNu indicates any other error.
	ErrorTypeNu = 2
)

type H

type H map[string]any

H is a shortcut for map[string]any

func (H) MarshalXML

func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML allows type H to be used with xml.Marshal.

type HandlerFunc

type HandlerFunc func(*Context)

HandlerFunc defines the handler used by gin middleware as return value.

func BasicAuth

func BasicAuth(accounts Accounts) HandlerFunc

BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where the key is the user name and the value is the password.

func BasicAuthForRealm

func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc

BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where the key is the user name and the value is the password, as well as the name of the Realm. If the realm is empty, "Authorization Required" will be used by default. (see http://tools.ietf.org/html/rfc2617#section-1.2)

func Bind

func Bind(val any) HandlerFunc

Bind is a helper function for given interface object and returns a Gin middleware.

func CustomRecovery

func CustomRecovery(handle RecoveryFunc) HandlerFunc

CustomRecovery returns a middleware that recovers from any panics and calls the provided handle func to handle it.

func CustomRecoveryWithWriter

func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc

CustomRecoveryWithWriter returns a middleware for a given writer that recovers from any panics and calls the provided handle func to handle it.

func ErrorLogger

func ErrorLogger() HandlerFunc

ErrorLogger returns a HandlerFunc for any error type.

func ErrorLoggerT

func ErrorLoggerT(typ ErrorType) HandlerFunc

ErrorLoggerT returns a HandlerFunc for a given error type.

func Logger

func Logger() HandlerFunc

Logger instances a Logger middleware that will write the logs to gin.DefaultWriter. By default, gin.DefaultWriter = os.Stdout.

func LoggerWithConfig

func LoggerWithConfig(conf LoggerConfig) HandlerFunc

LoggerWithConfig instance a Logger middleware with config.

func LoggerWithFormatter

func LoggerWithFormatter(f LogFormatter) HandlerFunc

LoggerWithFormatter instance a Logger middleware with the specified log format function.

func LoggerWithWriter

func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc

LoggerWithWriter instance a Logger middleware with the specified writer buffer. Example: os.Stdout, a file opened in write mode, a socket...

func Recovery

func Recovery() HandlerFunc

Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.

func RecoveryWithWriter

func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc

RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.

func WrapF

func WrapF(f http.HandlerFunc) HandlerFunc

WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.

func WrapH

func WrapH(h http.Handler) HandlerFunc

WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.

type HandlersChain

type HandlersChain []HandlerFunc

HandlersChain defines a HandlerFunc slice.

func (HandlersChain) Last

func (c HandlersChain) Last() HandlerFunc

Last returns the last handler in the chain. i.e. the last handler is the main one.

type IRouter

type IRouter interface {
	IRoutes
	Group(string, ...HandlerFunc) *RouterGroup
}

IRouter defines all router handle interface includes single and group router.

type IRoutes

IRoutes defines all router handle interface.

type LogFormatter

type LogFormatter func(params LogFormatterParams) string

LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter

type LogFormatterParams

type LogFormatterParams struct {
	Request *http.Request

	// TimeStamp shows the time after the server returns a response.
	TimeStamp time.Time
	// StatusCode is HTTP response code.
	StatusCode int
	// Latency is how much time the server cost to process a certain request.
	Latency time.Duration
	// ClientIP equals Context's ClientIP method.
	ClientIP string
	// Method is the HTTP method given to the request.
	Method string
	// Path is a path the client requests.
	Path string
	// ErrorMessage is set if error has occurred in processing the request.
	ErrorMessage string

	// BodySize is the size of the Response Body
	BodySize int
	// Keys are the keys set on the request's context.
	Keys map[string]any
	// contains filtered or unexported fields
}

LogFormatterParams is the structure any formatter will be handed when time to log comes

func (*LogFormatterParams) IsOutputColor

func (p *LogFormatterParams) IsOutputColor() bool

IsOutputColor indicates whether can colors be outputted to the log.

func (*LogFormatterParams) MethodColor

func (p *LogFormatterParams) MethodColor() string

MethodColor is the ANSI color for appropriately logging http method to a terminal.

func (*LogFormatterParams) ResetColor

func (p *LogFormatterParams) ResetColor() string

ResetColor resets all escape attributes.

func (*LogFormatterParams) StatusCodeColor

func (p *LogFormatterParams) StatusCodeColor() string

StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.

type LoggerConfig

type LoggerConfig struct {
	// Optional. Default value is gin.defaultLogFormatter
	Formatter LogFormatter

	// Output is a writer where logs are written.
	// Optional. Default value is gin.DefaultWriter.
	Output io.Writer

	// SkipPaths is an url path array which logs are not written.
	// Optional.
	SkipPaths []string

	// Skip is a Skipper that indicates which logs should not be written.
	// Optional.
	Skip Skipper
}

LoggerConfig defines the config for Logger middleware.

type Negotiate

type Negotiate struct {
	Offered  []string
	HTMLName string
	HTMLData any
	JSONData any
	XMLData  any
	YAMLData any
	Data     any
	TOMLData any
}

Negotiate contains all negotiations data.

type Param

type Param struct {
	Key   string
	Value string
}

Param is a single URL parameter, consisting of a key and a value.

type Params

type Params []Param

Params is a Param-slice, as returned by the router. The slice is ordered, the first URL parameter is also the first slice value. It is therefore safe to read values by the index.

func (Params) ByName

func (ps Params) ByName(name string) (va string)

ByName returns the value of the first Param which key matches the given name. If no matching Param is found, an empty string is returned.

func (Params) Get

func (ps Params) Get(name string) (string, bool)

Get returns the value of the first Param which key matches the given name and a boolean true. If no matching Param is found, an empty string is returned and a boolean false .

type RecoveryFunc

type RecoveryFunc func(c *Context, err any)

RecoveryFunc defines the function passable to CustomRecovery.

type ResponseWriter

type ResponseWriter interface {
	http.ResponseWriter
	http.Hijacker
	http.Flusher
	http.CloseNotifier

	// Status returns the HTTP response status code of the current request.
	Status() int

	// Size returns the number of bytes already written into the response http body.
	// See Written()
	Size() int

	// WriteString writes the string into the response body.
	WriteString(string) (int, error)

	// Written returns true if the response body was already written.
	Written() bool

	// WriteHeaderNow forces to write the http header (status code + headers).
	WriteHeaderNow()

	// Pusher get the http.Pusher for server push
	Pusher() http.Pusher
}

ResponseWriter ...

type RouteInfo

type RouteInfo struct {
	Method      string
	Path        string
	Handler     string
	HandlerFunc HandlerFunc
}

RouteInfo represents a request route's specification which contains method and path and its handler.

type RouterGroup

type RouterGroup struct {
	Handlers HandlersChain
	// contains filtered or unexported fields
}

RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix and an array of handlers (middleware).

func (*RouterGroup) Any

func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes

Any registers a route that matches all the HTTP methods. GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.

func (*RouterGroup) BasePath

func (group *RouterGroup) BasePath() string

BasePath returns the base path of router group. For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".

func (*RouterGroup) DELETE

func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes

DELETE is a shortcut for router.Handle("DELETE", path, handlers).

func (*RouterGroup) GET

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes

GET is a shortcut for router.Handle("GET", path, handlers).

func (*RouterGroup) Group

func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup

在现有 group的基础上,新创建一个*RouterGroup

func (*RouterGroup) HEAD

func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes

HEAD is a shortcut for router.Handle("HEAD", path, handlers).

func (*RouterGroup) Handle

func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes

Handle registers a new request handle and middleware with the given path and method. The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes. See the example code in GitHub.

For GET, POST, PUT, PATCH and DELETE requests the respective shortcut functions can be used.

This function is intended for bulk loading and to allow the usage of less frequently used, non-standardized or custom methods (e.g. for internal communication with a proxy).

func (*RouterGroup) Match

func (group *RouterGroup) Match(methods []string, relativePath string, handlers ...HandlerFunc) IRoutes

Match registers a route that matches the specified methods that you declared.

func (*RouterGroup) OPTIONS

func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes

OPTIONS is a shortcut for router.Handle("OPTIONS", path, handlers).

func (*RouterGroup) PATCH

func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes

PATCH is a shortcut for router.Handle("PATCH", path, handlers).

func (*RouterGroup) POST

func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes

POST is a shortcut for router.Handle("POST", path, handlers).

func (*RouterGroup) PUT

func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes

PUT is a shortcut for router.Handle("PUT", path, handlers).

func (*RouterGroup) Static

func (group *RouterGroup) Static(relativePath, root string) IRoutes

Static serves files from the given file system root. Internally a http.FileServer is used, therefore http.NotFound is used instead of the Router's NotFound handler. To use the operating system's file system implementation, use :

router.Static("/static", "/var/www")

func (*RouterGroup) StaticFS

func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes

StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead. Gin by default uses: gin.Dir()

func (*RouterGroup) StaticFile

func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes

StaticFile registers a single route in order to serve a single file of the local filesystem. router.StaticFile("favicon.ico", "./resources/favicon.ico")

func (*RouterGroup) StaticFileFS

func (group *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes

StaticFileFS works just like `StaticFile` but a custom `http.FileSystem` can be used instead.. router.StaticFileFS("favicon.ico", "./resources/favicon.ico", Dir{".", false}) Gin by default uses: gin.Dir()

func (*RouterGroup) Use

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes

Use adds middleware to the group, see example code in GitHub.

type RoutesInfo

type RoutesInfo []RouteInfo

RoutesInfo defines a RouteInfo slice.

type Skipper

type Skipper func(c *Context) bool

Skipper is a function to skip logs based on provided Context

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL