Gin 基本使用 原理篇

扩展知识:

go mod

类似于我们 Python pip 做依赖管理的,每个项目可能都会使用一些外部包,外步包有很多版本
  • go mod就是帮助我们自动管理你们的包和版本号的
  • 如果没有go mod别人如何才能运行你们的代码

外部的包:其他人封装好的,实现特定功能的代码

常用命令:
  • go get github.com/gin-gonic/gin // GitHub上,或者其他仓库里的代码下载到本地,直接导入 就可以是用了。
  • go mod tidy // 更新我们的依赖(第一次运行项目,执行这个命令他会将项目中所有的外部包 一次性的全部下载到本地

Gin入门


http/net既可以做服务端,又可以做客户端,但是gin框架一般只做服务端来使用,如果要发起请求还是去使用http/net包,而且相对于http/net来说添加了很多的功能,比如日志功能。

1.介绍(主要帮我我们写api接口)

  • Gin是一个golang的微框架(只处理api),封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点(beego很重,前端后端都写在框架里面,同时还自带数据库框架)
  • 对于golang而言,web框架的依赖要远比PythonJava之类的要小。自身的 net/http 足够简单,性能也非常不错(当不需要去写web站点的时候,如很多的平台,那么就不需要用到web框架。用go自身携带的这些包就可以写日常的一些脚本了)
  • 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。
2.gin特点和特性
  • 速度:gin之所以被许多企业和团队使用第一个原因是因为其速度快,性能表现出众
  • 中间件:和iris类型,gin在处理请求的时候,支持中间件的操作,方便编码处理
  • 路由:在gin中可以非常简单的实现路由解析功能,并包含路由组解析功能
  • 内置渲染:gin支持json,xml,html等多种数据格式的渲染,并且提供了方便操作的api
  • Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
  • 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错
  • 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范

 

3. 快速写一个
下面可以看到request和response相关操作都可以直接使用c *gin.Context去操作,直接使用c就可以实现了
说明:gin. Default  还有gin. New
关系:default 内部也是调用的 New 但是会默认加上 Logger 和 Recovery中间件 
软件包:gin
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
type HandlerFunc func(*Context)

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {

    	// 1.创建 (实例化gin.Engine结构体对象,指针类型,相当于服务器引擎) 
        //拿到一个 *gin.Engine 
	    g := gin.Default()

    	// 2.注册路由规则以及绑定执行的函数
	    //gin.Context 请求上下文,封装了request和response,之后的请求和响应直接使用context就可以实现
	    g.GET("/", func(c *gin.Context) {

       //拿到request请求的header
		fmt.Println(c.GetHeader("Accept-Encoding"))

       //拿到request请求的路径
       fmt.Println("请求路径:", c.FullPath())
     
       //下面是返回,设置到response里面,两种返回方式
		c.String(http.StatusOK, "hello world")
	    c.Writer.Write([]byte("hello,gin"))

         c.JSON(200, gin.H{   //响应页面
              "msg": "pong",
         }

	})


	// 3.监听端口,默认在8080
	//Run("里面不指定端口号默认为8080")
	g.Run()

	if err := r.Run(":8000"); err != nil {
		log.Fatal(err.Error())
	}
}


		c.Header("name", "lucas")  //设置响应头部
		c.Request.Header.Add("content-type","application/json") //设置请求头部

Gin工作流程


 1. 核心概念

type Engine struct {
    RouterGroup
    trees                  methodTrees
}
  • Engine  容器对象,整个框架的基础
  • Engine.trees  负责存储路由和handle方法的映射,采用类似字典树的结构(一个存储路由和方法的地方)
  • Engine.RouterGroup  其中的Handlers存储着所有中间件(其实也就是有一个链表,将中间件和handler处理方法全部都存进去)
  • Context 上下文对象,负责处理 请求和回应 ,其中的 handlers 是存储处理请求时中间件和处理方法的(在使用http包的时候,在写handler方法对时候要去写 http response request这些,因为要去从request里面去拿请求的参数,或者header,在响应的时候是使用的是response writer,那么其实context其实就是封装了r和w)

gin.Default会去new一个gin的实例,然后再去Use中间件。之后去注册路由的时候将路由加入到路由树里面。最后r.run其实就是调用了http.ListenAndServe(":8081", nil)

2. 请求处理流程

首先我是一个用户,我去发起请求,是哪个路径。服务端这边监听和接受信息。然后会去看看有没有中间件,如果有它会先去处理中间件,其实也就是实际处理路由之前需要处理的。其实也就是放了一个全局的拦截器,过滤器。先要处理完发过来的信息,处理完之后传给下一层。

经过中间件处理完之后会去丢给具体处理的哪个路由,哪个url,匹配到了之后再去处理handler方法。handler里面具体的逻辑就是大家去写,比如是否要去连接数据库。

处理完之后,再去返回给中间件去做一层处理,可能有有可能没有。中间件处理完之后再去将这个结果返回给用户。

总结:接收到消息之后不是立刻路由处理,首先是中间件处理,先过一层中间件,中间件处理完毕之后再到路由,处理完之后再到handler的业务处理逻辑,无论是你去连接redis还是mysql都根据你的需求去写。handler处理完之后再去给中间件去处理。

大部分情况下可能只有一层中间件,返回的时候是不需要的。

Gin原理解析


参考资料: http://v5blog.cn/pages/dd7d5a/
1. 先看 gin.Default()
Default() New() 几乎一模一样 , 就是调用了 gin 内置的 Logger(), Recovery() 中间件
// Default返回一个已经附加了Logger和Recovery中间件的Engine实例
func Default() *Engine { 

    debugPrintWARNINGDefault() 
    engine := New() // 默认实例,默认实例是不会去注册中间件的

    // 注册中间建,中间件的是一个函数,最终只要返回一个 type HandlerFunc func(*Context) 就以 
    engine.Use(Logger(), Recovery())  //一个是日志中间件和处理Panic的
    // 默认注册的两个中间件 

    return engine 
}

2. engine := New() 初始化

通过调用 gin.New() 方法来实例化 Engine 容器
  1. 初始化了Engine
  2. RouterGroupHandlers(数组,存放中间件和处理函数的)设置成nil, basePath设置成 /
  3. 为了使用方便, RouteGroup里面也有一个Engine指针, 这里将刚刚初始化的engine赋值给了 RouterGroupengine指针
  4. 为了防止频繁的context GC造成效率的降低, Engine里使用了sync.Pool, 专门存储ginContext(当你去写了api接口,你会接受到很多的请求,你写的这些接口是都需要去使用的,那么每一个请求就有一个context上下文,因为每个请求对request和response都可能不一样,这些上下文通过sync.pool去处理,避免大量的内存消耗。常用的上下文存在sync.pool里面,要使用的话直接在内存池里面去拿,如果长期不使用它会帮你自动释放,可以理解为自动对垃圾回收
func New() *Engine { 
    debugPrintWARNINGNew() 
    // Engine 容器对象,整个框架的基础 

engine := &Engine{ // 初始化语句 

    // Handlers 全局中间件组在注册路由时使用 
    RouterGroup: RouterGroup{ // Engine.RouterGroup,其中的Handlers存储着所有中间件                             
    Handlers: nil, //没有任何处理方法和中间件的,注册路由和注册中间件都是在注册路由之后加上的
    basePath: "/", 
    root: true, 
}, 

    // 树结构,保存路由和处理方法的映射,这里会存储9种请求方法对,根据每种请求方法分开
    trees: make(methodTrees, 0, 9), 
}

engine.RouterGroup.engine = engine return engine //初始化完成之后,它又将初始化实例放到结构体里面了
}

3. engine.Use() 注册中间件

  • gin框架中的中间件设计很巧妙,我们可以首先从我们最常用的 r := gin.Default() 的Default 函数开始看
  • 它内部构造一个新的 engine 之后就通过 Use() 函数注册了
  • Logger 中间件和 Recovery 中间件 Use()就是gin的引入中间件的入口了
  • 仔细分析这个函数, 不难发现Use()其实是在给RouteGroup引入中间件的
  • 具体是如何让中间件在RouteGroup上起到作用的, 等说到RouteGroup再具体说.
func Default() *Engine {
    debugPrintWARNINGDefault()
    engine := New()
    engine.Use(Logger(), Recovery()) // 默认注册的两个中间件
    return engine
}

 gin.use() 调用 RouterGroup.Use() 往 RouterGroup.Handlers 写入记录

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    engine.RouterGroup.Use(middleware...)
    engine.rebuild404Handlers() //注册404处理方法
    engine.rebuild405Handlers() //注册405处理方法
    return engine
}


// 其中`Handlers`字段就是一个数组,用来存储中间件
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}

组成一条处理函数链条 HandlersChain

  • 也就是说,我们会将一个路由的中间件函数和处理函数结合到一起组成一条处理函数链条HandlersChain
  • 而它本质上就是一个由HandlerFunc组成的切片 type HandlersChain []HandlerFunc

也就是说当我们一个请求进来是先经过中间件,这里其实就是一个链表。先经过注册的这些中间件,那如果这里没有就没有,但是default就默认的帮你注册了两个中间件。

注册了中间件之后是一定先经过中间件,比handler先执行,匹配到路径然后去处理handler,中间件在处理方法之前执行的。

中间件比如做一些日志处理,做一些全局的权限校验,比如请求接口,我得看有没有权限。

4. r.GET() 注册路由

r.GET("/", func(c *gin.Context) {
    c.String(http.StatusOK, "hello World!")
})

通过Get方法将路由和处理视图函数注册

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

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers

    HandlersChain) IRoutes {
    absolutePath := group.calculateAbsolutePath(relativePath)
    handlers = group.combineHandlers(handlers) // 将处理请求的函数与中间件函数结合
    group.engine.addRoute(httpMethod, absolutePath, handlers) // ---- 调用 addRoute方法
注册路由
    return group.returnObj()
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Gin框架中使用JWT(JSON Web Token)可以实现份验证和授权功能。JWT是一种用于在网络应用间传递信息的安全方法,它由三部分组成:header、payload和signature[^1]。 下面是在Gin框架中使用JWT的示例代码[^2]: 1. 导入所需的包: ```go import ( "github.com/gin-gonic/gin" "github.com/dgrijalva/jwt-go" ) ``` 2. 定义JWT的密钥: ```go var jwtKey = []byte("your_secret_key") ``` 3. 创建一个JWT的Claims结构体,用于存储用户的信息: ```go type Claims struct { Username string `json:"username"` jwt.StandardClaims } ``` 4. 创建一个处理登录请求的路由: ```go func login(c *gin.Context) { var loginData LoginData if err := c.ShouldBindJSON(&loginData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) return } // 验证用户名和密码 if loginData.Username == "admin" && loginData.Password == "password" { // 创建JWT的Claims claims := &Claims{ Username: loginData.Username, StandardClaims: jwt.StandardClaims{ ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 设置过期时间 }, } // 创建JWT token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(jwtKey) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"}) return } // 返回JWT给客户端 c.JSON(http.StatusOK, gin.H{"token": tokenString}) } else { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"}) } } ``` 5. 创建一个需要身份验证的路由: ```go func protectedRoute(c *gin.Context) { // 从请求头中获取JWT authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Missing authorization header"}) return } // 解析JWT tokenString := authHeader[7:] // 去除Bearer前缀 claims := &Claims{} token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { return jwtKey, nil }) if err != nil { if err == jwt.ErrSignatureInvalid { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"}) return } c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid token"}) return } if !token.Valid { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) return } // 验证通过,继续处理请求 c.JSON(http.StatusOK, gin.H{"message": "Protected route"}) } ``` 6. 在路由中注册处理函数: ```go func main() { r := gin.Default() r.POST("/login", login) r.GET("/protected", protectedRoute) r.Run(":8080") } ``` 以上代码演示了在Gin框架中使用JWT进行身份验证和授权的基本流程。用户可以通过发送登录请求获取JWT,然后在需要身份验证的路由中将JWT放入请求头中进行验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值