Golang之Gin框架源码解读——第三章

Gin是使用Go语言编写的高性能的web服务框架,根据官方的测试,性能是httprouter的40倍左右。要使用好这套框架呢,首先我们就得对这个框架的基本结构有所了解,所以我将从以下几个方面来对Gin的源码进行解读。

  • 第一章:Gin是如何储存和映射URL路径到相应的处理函数的
  • 第二章:Gin中间件的设计思想及其实现
  • 第三章:Gin是如何解析客户端发送请求中的参数的
  • 第四章:Gin是如何将各类格式(JSON/XML/YAML等)数据解析返回的

Gin Github官方地址

Gin是如何解析客户端发送请求中的参数的

事实上,Gin也是基于http包封装来实现的网络通信,底层仍旧使用的是http.ListenAndServe来创建的监听端口和服务,只不过将接收到的数据解析为GinContext上下文后,最终再传递到type HandlerFunc func(*Context)处理函数中去的。

再了解一个大致的数据处理过程之后,我们就从Gin的监听入口开始逐渐摸索。

建立监听服务

if err := router.Run();err != nil {
   
		log.Println("something error");
}

func (engine *Engine) Run(addr ...string) (err error) {
   
	defer func() {
    debugPrintError(err) }()

	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine)
	return
}

func ListenAndServe(addr string, handler Handler) error {
   
	server := &Server{
   Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

通过上面这个过程可以了解到Ginhttp通信框架建立联系是通过engine *Engine实现的,同时ListenAndServe要求传入的是一个Handler类型的对象,而该对象定义如下:

type Handler interface {
   
	ServeHTTP(ResponseWriter, *Request)
}

这咋一看,瞬间就明白了许多,ResponseWriter, *Request这两个参数一目了然——请求与响应流http包就是底层处理过后将这两个数据通过该接口传递到Gin框架内部的,所以我们找到该接口的实现。

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

服务处理

在正式开始了解这个处理过程之前,我们先来了解一下Context这个贯穿整个Gin框架的上下文对象,在C/S通信过程中所有的数据都保存在这个对象中了。

type Context struct {
   
    //响应输出流(私有,供框架内部数据写出)
    writermem responseWriter
    //客户端发送的所有信息都保存在这个对象里面
    Request   *http.Request
    //响应输出流(公有,供给处理函数写出)
    // 在初始化后,由writermem克隆而来的
	Writer    ResponseWriter

    //保存解析得到的参数,路径中的REST参数
    Params   Params
    //该请求对应的处理函数链,从树节点中获取
    handlers HandlersChain
    //记录已经被处理的函数个数
    index    int8
    //当前请求的完整路径
	fullPath string
    //Gin的核心引擎
	engine *Engine
    //并发读写锁
	KeysMutex *sync.RWMutex

	//用于保存当前会话的键值对,用于不同处理函数中传递
	Keys map[string]interface{
   }

	//处理函数链输出的错误信息
	Errors errorMsgs

	//客户端希望接受的数据类型,如:json、xml、html
	Accepted []string

    //存储URL中的查询参数,如:/test?name=jhon&age=11
    // 这样的参数储存在这个对象里
	queryCache url.Values

	//这个用于存储POST/PATCH等提交的body中的参数
	formCache url.Values

    //用来限制第三方 Cookie,一个int值,有Strict、Lax、None
    // Strict:只有当前网页的 URL 与请求目标一致,才会带上 Cookie
    // Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,
    // 但是导航到目标网址的 Get 请求除外
    // 设置了Strict或Lax以后,基本就杜绝了 CSRF 攻击
	sameSite http.SameSite
}

在了解完Context后,我们来进入正式的数据解析过程:

func (engine *Engine) handleHTTPRequest(c *Context) {
   
    //获取客户端的http请求方法
    httpMethod := c.Request.Method
    //获取请求的URL地址,这里的URL是进过处理的
    rPath := c.Request.URL.Path
    //是否不启动字符转义
    unescape := false
    //判断是否启用原URL,未转义字符
	if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
   
		rPath = c.Request.URL.RawPath
		unescape = engine.UnescapePathValues
	}

    //判断是否需要移除多余的分隔符"/"
	if engine.RemoveExtraSlash {
   
		rPath = cleanPath(rPath)
	}

	
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
   
		if t[i].method != httpMethod {
   
			continue
        }
        //首先获取到指定HTTP方法的搜索树的根节点
		root := t[i].root
		//从根节点开始搜索匹配该路径的节点
        value := root.getValue(rPath, c.Params, unescape)
        //将节点中的存储的信息,拷贝到Context上下文中
		if value.handlers != nil {
   
			c.handlers = value.handlers
			c.Params = value.params
            c.fullPath = value.fullPath
            //这里就是在遍历执行处理函数链
            // func (c *Context) Next() {
   
            //     c.index++
            //     for c.index < int8(len(c.handlers)) {
   
            //         c.handlers[c.index](c)
            //         c.index++
            //     }
            // }
            c.Next()
            //写出响应状态码
			c.writermem.WriteHeaderNow()
			return
        }
        //如果没有找到对应的匹配节点,则考虑是否是以下的特殊情况
		if httpMethod != "CONNECT" && rPath != "/" {
   
            //如果启动自动重定向,删除最后的"/"并重定向
			if value.tsr 
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值