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的主要流程和主体思路。


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

查看所有标签

猜你喜欢:

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

商业的常识

商业的常识

申音 / 山西经济出版社 / 2011-7-1 / 35.00元

★为什么美国没有史玉柱,中国没有乔布斯? ★什么是“对的行业”、“错的行业”? ★我们需要什么样的营销? ★老板为什么要读商学院? ★山寨公司还需要管理吗? ★资源问题是个“伪问题”? ★别把商业模式当成葵花宝典 ★给海归技术创业兄弟的九个忠告 ★在一个不伟大的行业里,做一个伟大的公司 ★是什么让互联网遭遇了有史以来最鸡犬不宁的一战?一起来看看 《商业的常识》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具