Go Gin源码学习(二)

栏目: Go · 发布时间: 5年前

内容简介:上一篇学习了Gin框架的总体流程,但是自己查看源代码中被许多的零散小功能影响了主体流程的研究。所以觉得可以模仿Gin框架,自己写一个最简单仅仅含有主流程的demo。可以让我们更加深入了解Gin自己也可以再模仿的过程中更加了解源码。这个demo是我自己对Gin初步理解之后模仿写的,其中只包含主要功能结果

开场

上一篇学习了Gin框架的总体流程,但是自己查看源代码中被许多的零散小功能影响了主体流程的研究。所以觉得可以模仿Gin框架,自己写一个最简单仅仅含有主流程的demo。可以让我们更加深入了解Gin自己也可以再模仿的过程中更加了解源码。

功能

这个demo是我自己对Gin初步理解之后模仿写的,其中只包含主要功能

  • 创建路由
  • 新建group
  • 添加中间件
  • 启动http服务,接受客户端请求并返回简单数据 Demo中的路由兵没有使用Gin的tree只是简单的使用了map来实现,基数树是一个相对独立逻辑准备之后单独再学习

代码

package mygin

import (
	"fmt"
	"net/http"
	"path"
	"sync"
)

//上下文context 简单的他添加response request engine指针 isabort就可以支持最简单的流程
type Context struct {
	Request *http.Request
	ResponseWrite http.ResponseWriter
	engine *Engine
	isAbort bool
}

type HandlerFun func(ctx *Context)

type HandlerList []HandlerFun

type Engine struct {
	RouterGroup
	Handlers []HandlerFun
	router map[string]HandlerList
	pool sync.Pool
}

type Message struct {
	Message string
}

type IRouter interface {
	Use(...HandlerFun) IRouter
	GET(string, ...HandlerFun) IRouter
	Group(string, ...HandlerFun) *RouterGroup
}

type RouterGroup struct {
	Handlers []HandlerFun
	engine *Engine
	basePath string
}

func NewEngine()(*Engine){
	en := new(Engine)
	en.router = make(map[string]HandlerList)
	en.pool.New = func() interface{} {
		return en.allocateContext()
	}
	en.RouterGroup = RouterGroup{
		basePath:"/",
		Handlers:nil,
		engine:en,
	}

	return en
}

func (engine *Engine)Run(addr string)(err error){
	fmt.Println("Listening and serving HTTP on", addr)
	err = http.ListenAndServe(addr, engine)
	return
}

//继承http包中的handler接口,在run中即可传入engine
func (engine *Engine)ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.pool.Get().(*Context)
	c.ResponseWrite = w
	c.Request = req
	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

//客户端请求之后具体执行的函数 之前文章所说的获取所有handler 一个个执行
// 这里简单用了for循环 判断isabort属性来判断是否停止
func (engine *Engine) handleHTTPRequest(c *Context){
	httpMethod := c.Request.Method
	path := c.Request.URL.Path

	if handlers,ok := engine.router[httpMethod + "^" + path];ok{
		for _,fu := range handlers{
			fu(c)
			if c.isAbort{
				return
			}
		}
	}
}

func (engine *Engine) allocateContext() *Context{
	return &Context{engine:engine}
}

func (engine *Engine)addRoute(httpMethod, absolutePath string, handlers HandlerList){
	engine.router[httpMethod + "^" + absolutePath] = handlers
}

//添加group方法 设置group的basepath 和handler
func (routerGroup *RouterGroup)Group(path string,handlers ...HandlerFun) *RouterGroup{
	rg := RouterGroup{}
	rg.Handlers = routerGroup.CombineHandlers(handlers)
	rg.basePath = path
	rg.engine = routerGroup.engine

	return &rg
}

func (routerGroup *RouterGroup)Use(handlers ...HandlerFun) IRouter{
	routerGroup.Handlers = append(routerGroup.Handlers, handlers...)
	return routerGroup
}

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

func joinPaths(absolutePath, relativePath string) string {
	if relativePath == ""{
		return absolutePath
	}

	finalPath := path.Join(absolutePath,relativePath)

	appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath)!='/'
	if appendSlash{
		return finalPath + "/"
	}

	return finalPath
}

//工具方法 获取字符串最后一个字符
func lastChar(str string) uint8 {
	if str ==""{
		panic("The length of the string can't be 0")
	}

	return str[len(str)-1]
}

//计算路径合并handler 然后添加到map中
func (group *RouterGroup)handle(httpMethod, relativePath string, handlers HandlerList) IRouter{
	absolutePath := group.calculateAbsolutePath(relativePath)
	handlers = group.CombineHandlers(handlers)
	group.engine.addRoute(httpMethod, absolutePath, handlers)

	return group
}

//合并handler 之后返回
func (group *RouterGroup)CombineHandlers(handlers HandlerList)HandlerList{
	finalSize := len(group.Handlers) + len(handlers)
	mergedHandler := make(HandlerList, finalSize)
	copy(mergedHandler, group.Handlers)
	copy(mergedHandler[len(group.Handlers):], handlers)
	return mergedHandler
}

//添加get method路由
func (group *RouterGroup)GET(path string, handlers ...HandlerFun)(IRouter){
	group.handle("GET", path, handlers)

	return group
}

测试

func TestEngine_Run(t *testing.T) {
	router := NewEngine()
	router.GET("/test", func(ctx *Context) {
		fmt.Println("get request")
		//这边可以尝试拿一下参数 在gin中获取参数提供了很多的方法
		//这些不是主流程就没有在这里体现 有兴趣可以看一下源码其实也没有想象中的复杂
		//这边就先获取一下get参数
		pm := ctx.Request.URL.Query()
		if v,ok := pm["id"];ok{
			fmt.Println("request url", ctx.Request.URL.String()," parameter id value =",v)
		}
		ctx.ResponseWrite.WriteHeader(200)
		r := render.JSON{Data:"success"}
		r.WriteContentType(ctx.ResponseWrite)

		if err := r.Render(ctx.ResponseWrite); err != nil{
			panic(err)
		}
	})
	router.Run(":2222")
}

结果

//在console中,得到了客户端的请求并打印参数
Listening and serving HTTP on :2222
get request
request url /test?id=2  parameter id value = [2]
//客户端请求之后获取到了 success 的返回值
http://localhost:2222/test?id=2
"success"

总结

这个demo大概100多行代码,仅仅实现了Gin最最小的功能。还有大部分的之前提到的功能都未实现,但是只是这100多行代码就已经能看出Gin的主要流程和主体思路。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

劫持

劫持

玛丽•K. 斯温格尔(Mari K. Swingle) / 邓思渊 / 中信出版集团股份有限公司 / 2018-5-1 / CNY 59.00

《劫持》是一本探讨人与科技的关系的书,基于一位心理学博士20年的临床经验及其作为神经认知科学研究者的脑—电研究成果。在这本面向大众的科普书中,作者以深入浅出的方式,探讨了手机、电脑等便携式数字设备及让人“永不下线”的互联网对现代人尤其是青少年大脑的影响,从神经认知科学和精神分析的角度,有力地证明了数字媒介与大脑和人类行为的关系,探讨了手机等如何对人的大脑进行劫持或操控,并给出了自己作为从业医师的实......一起来看看 《劫持》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具