Gin框架从入门到实战:核心用法与最佳实践

为什么选择Gin框架?

Gin 是一个基于 Go 语言的高性能 Web 框架,具备以下优势:

  • 轻量高效:底层依赖 net/http,性能接近原生。
  • 简洁优雅:API 设计友好,支持路由分组、中间件链、参数绑定等特性。
  • 生态丰富:内置 JSON 解析、日志记录、错误恢复等实用功能,社区插件生态完善。
  • 无论是构建 RESTful API 还是全栈应用,Gin 都能显著提升开发效率。

安装

要安装Gin软件包,您需要安装Go并首先设置Go工作区。

  • 首先需要安装Go(需要1.10+版本),然后可以使用下面的Go命令安装Gin。
go get -u github.com/gin-gonic/gin
  • 将其导入您的代码中:
import "github.com/gin-gonic/gin"

基础示例

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {// 1.创建 (实例化gin.Engine结构体对象)r := gin.Default()// 2.绑定路由规则,执行的函数// gin.Context,封装了request和responser.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "Hello, Gin!")})// 3.监听端口,默认在8080//  Run("里面不指定端口号默认为8080")r.Run(":8080")
}

运行后访问 http://localhost:8080,即可看到返回的字符串。

Gin工工作作流流程

核心概念

  • Engine 容器对象,整个框架的基础
  • Engine.tree 负责存储路由和handle方法的映射,采用类似于字典树的结构
  • Engine.RouterGroup 其中handlers存储着所有中间件
  • Context 上下文对象,负责处理 请求回应 ,其中handles的存储处理请求时中间件和处理方法的

在这里插入图片描述

请求处理 流程

在这里插入图片描述

GIN启动流程

Gin初始化----> Use 中间件  ---->  注册Routers路由 ----> RUN()启动

Gin原原理解析

参考资料:http://v5blog.cn/pages/dd7d5a/

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()) // 默认注册的两个中间件return engine
}

engine := New() 初初始化始化

通过调用 gin.New() 方法来实例化 Engine容器

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

在这里插入图片描述

路由与参数处理

无参路由

func HelloWorldhandler(ctx *gin.Context){ctx.JSON(200, gin.H{"message": "Hello World",})
}func main() {// 创建一个默认的路由器router := gin.Default()// gin.Context是一个结构体,包含了请求和响应的细节, 封装request和responserouter.GET("/",HelloWorldhandler)// 路由重定向router.GET("/re", func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")})// 启动服务器router.Run(":8081")
}

动态路由参数

通过 :param 捕获 URL 中的变量:
(可以通过Context的Param方法来获取API参数)

r.GET("/book/:id", func(c *gin.Context) {bookID := c.Param("id")c.String(http.StatusOK, "书籍ID: %s", bookID)
})

查询参数

使用 Query 或 DefaultQuery 获取 URL 参数:
http://127.0.0.1:8000/user?name=zhangsan

r.GET("/user", func(c *gin.Context) {name := c.Query("name")role := c.DefaultQuery("role", "guest")c.JSON(200, gin.H{"name": name, "role": role})
})

ShouldBind参数绑定

通过 ShouldBind 自动解析请求体(支持 JSON、Form 等):

我们可以基于请求的 Content-Type 识别请求数据类型并利用反射机制
自动提取请求中 QueryString 、 form表单 、 JSON 、 XML 等参数到结构体中

type LoginForm struct {Username string `form:"username" binding:"required"`Password string `form:"password" binding:"required"`
}r.POST("/login", func(c *gin.Context) {var form LoginFormif err := c.ShouldBind(&form); err != nil {c.JSON(400, gin.H{"error": err.Error()})return}c.String(200, "登录成功: %s", form.Username)
})

完整代码案例

package mainimport ("fmt""github.com/gin-gonic/gin""net/http"
)// HelloWorldhandler 无参数路由处理函数
func HelloWorldhandler(ctx *gin.Context){ctx.JSON(200, gin.H{"message": "Hello World",})
}func GetBookDetailHandler(ctx *gin.Context )  {bookId := ctx.Param("id")ctx.String(http.StatusOK, fmt.Sprintf("成功获取书籍详情:%s", bookId))}func GetUserDetailHandlers(ctx *gin.Context)   {username := ctx.Query("username")ctx.String(http.StatusOK, fmt.Sprintf("成功获取用户详情:%s", username))
}type Login struct {Username string `form:"username" json:"username" binding:"required"`Password string `form:"password" json:"password" binding:"required"`
}func ResponseJsonHandler(c *gin.Context) {type Data struct {Msg string `json:"msg:"`Code int    `json:"code"`}d := Data{Msg: "success",Code: 200,}c.JSON(http.StatusOK, d)
}
func Loginhandler(c *gin.Context)  {var login Loginif err := c.ShouldBind(&login); err == nil {c.JSON(http.StatusOK, gin.H{"username": login.Username,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}
}func ResponseStringHandle(c *gin.Context)  {c.String(http.StatusOK, "Hello World")
}func main() {// 创建一个默认的路由器router := gin.Default()// gin.Context是一个结构体,包含了请求和响应的细节, 封装request和response// 无参数路由router.GET("/",HelloWorldhandler)// 返回字符串router.GET("/string", ResponseStringHandle)// 返回jsonrouter.GET("/json", ResponseJsonHandler)// 动态路由参数router.GET("/book/:id", GetBookDetailHandler)// 查询参数 Query 或 DefaultQueryrouter.GET("/user/", GetUserDetailHandlers)// 参数绑定router.POST("/login", Loginhandler)// 路由重定向router.GET("/re", func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")})// 启动服务器router.Run(":8081")
}

路由分发

为什么需要路由分发?

  • 我们一个项目有非常多的模块,如果全部写在一块导致代码结构混乱,不利于后续的扩展
  • 按照大的模块,每个模块有自己独立的路由,主路由可以再main.go中进行注册

项目结构

├── go.mod
├── go.sum
├── main.go
└── routers├── books.go└── users.go

main.go


import (
"days/routers"
"fmt"
"github.com/gin-gonic/gin"
)
func main() {router := gin.Default()// 全局中间件router.Use(MiddleWare())// 加载路由routers.LoadUsers(router)routers.LoadBooks(router)router.Run(":8081")
}

routers/users.go

package routers
import ("fmt""github.com/gin-gonic/gin""net/http""time"
)func LoadUsers(e *gin.Engine)  {e.GET("/user",MiddleWareOne(),UserHandler)
}func UserHandler(c *gin.Context) {fmt.Println("我是用户路由")time.Sleep(time.Second * 5)c.JSON(http.StatusOK,gin.H{"message" : "weclome user",})
}

routers/books.go

package routers
import (
"net/http"
"github.com/gin-gonic/gin"
)
func LoadBooks(e *gin.Engine) {e.GET("/book", GetBookHandler)
}
func GetBookHandler(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Book Router",})
}

在这里插入图片描述

中间件

在这里插入图片描述

  • Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。
  • 这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑
  • 比如登录认证、权限校验、数据分页、记录日志、耗时统计等

全局中间件

func Logger() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()c.Next()latency := time.Since(start)fmt.Printf("请求耗时: %v\n", latency)}
}func main() {r := gin.Default()r.Use(Logger()) // 全局生效r.GET("/", func(c *gin.Context) { /* ... */ })
}

局部中间件

func AuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {token := c.GetHeader("token")if token != "SECRET_KEY" {c.AbortWithStatusJSON(401, gin.H{"error": "身份验证失败"})}c.Next()}
}r.GET("/profile", AuthMiddleware(), func(c *gin.Context) {c.JSON(200, gin.H{"data": "用户信息"})
})

next()方法

  • 在中间件中调用next()方法,会从next()方法调用的地方跳转到Handler函数
  • Handler函数执行完成,若中间件还有部分代码未执行(中间件中next()之后的代码),则执行该代码
    在这里插入图片描述
package mainimport ("fmt""github.com/gin-gonic/gin""net/http""time"
)func main() {r := gin.Default()r.Use(Log(), RequestID())r.GET("/", func(c *gin.Context) {fmt.Println("app running  1")time.Sleep(time.Second * 5)c.String(http.StatusOK, "hello World!")})r.Run()
}
func Log() gin.HandlerFunc {return func(c *gin.Context) {fmt.Println("log start")c.Next()fmt.Println("log end")}
}
func RequestID() gin.HandlerFunc {return func(c *gin.Context) {fmt.Println("requestid start")c.Next()fmt.Println("requestid end")}
}

实现token认证

  • http://127.0.0.1:8080/index index首页无需token直接访问
  • http://127.0.0.1:8080/home home家目录需要对token进行验证,验证通过才可访问
package mainimport ("fmt""github.com/gin-gonic/gin"
)func AuthMiddleWare() func(c *gin.Context)  {return func(c *gin.Context){// 客户端携带token 有三种方式 1.放在请求头 2.放在请求体 3.放在url// token 验证成功,返回 c.next()才会继续,否则 c.Abort()token := c.Request.Header.Get("token")fmt.Println("获取token:",token)if token == "" {c.JSON(200, gin.H{"code": 401,"msg": "身份验证不通过",})c.Abort()}if token != "123456" {c.JSON(200, gin.H{"code": 401,"msg": "token错误",})c.Abort()}}
}func main()  {router := gin.Default()// 首页无需验证router.GET("/", func(c *gin.Context) {c.JSON(200, gin.H{"message": "首页",})})// Home页面需要验证router.GET("/home", AuthMiddleWare(), func(c *gin.Context) {c.JSON(200, gin.H{"message": "Home页面",})})router.Run(":8081")
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/71311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 3468. Find the Number of Copy Arrays

Leetcode 3468. Find the Number of Copy Arrays 1. 解题思路2. 代码实现 题目链接:3468. Find the Number of Copy Arrays 1. 解题思路 这一题的话思路上就是一个范围考察,显然,对于指定的copy方式,只要我们确定了第一个元素&…

VirtualBox虚拟机MacOS从Big Sur升级到Sequoia(失败)

VirtualBox虚拟机里安装好Big Sur版本,尝试升级到Sequoia,但是最终失败了。 软件升级 直接在系统偏好-软件更新里可以看到提示,提示可以升级到15版本Sequoia 点击同意,看能不能升级到Sequoia吧。升级前先用时光做了备份。 升级…

[杂学笔记]HTTP1.0和HTTP1.1区别、socket系列接口与TCP协议、传输长数据的时候考虑网络问题、慢查询如何优化、C++的垃圾回收机制

目录 1.HTTP1.0和HTTP1.1区别 2.socket系列接口与TCP协议 3.传输长数据的时候考虑网络问题 4.慢查询如何优化 5.C的垃圾回收机制 1.HTTP1.0和HTTP1.1区别 在连接方式上,HTTP1.0默认采用的是短链接的方式,就建立一次通信,也就是说即使在…

ANI AGI ASI的区别

‌‌ANI、‌AGI、‌ASI的区别主要体现在定义、特点和应用场景上‌: 1. ANI(狭义人工智能 Artificial narrow intelligence)‌: ‌定义‌:ANI,也被称为弱人工智能,是指专门设计用于执行特定任务…

用OpenCV写个视频播放器可还行?(Python版)

引言 提到OpenCV,大家首先想到的可能是图像处理、目标检测,但你是否想过——用OpenCV实现一个带进度条、倍速播放、暂停功能的视频播放器?本文将通过一个实战项目,带你深入掌握OpenCV的视频处理能力,并解锁以下功能&a…

leetcode日记(77)子集Ⅱ

不知道为什么看到这道题就很头痛…… 其实只要掌握nums不包含重复元素的情况下的代码就行了。 若nums不能包含重复元素&#xff0c;那么使用回溯很容易就能写出来&#xff1a; class Solution {void hs(vector<int> v,int x,vector<int> r,vector<vector<…

通俗版解释:分布式和微服务就像开餐厅

一、分布式系统&#xff1a;把大厨房拆成多个小厨房 想象你开了一家超火爆的餐厅&#xff0c;但原来的厨房太小了&#xff1a; 问题&#xff1a;一个厨师要同时切菜、炒菜、烤面包&#xff0c;手忙脚乱还容易出错。 解决方案&#xff1a; 拆分成多个小厨房&#xff08;分布式…

StarRocks-fe工程在Cursor中不能识别为Java项目

SR简介 StarRocks 是一款高性能分析型数据库&#xff0c;支持实时、多维度、高并发的数据分析。本指南旨在解决在使用 VSCode 或 Cursor 开发 StarRocks 后端项目时遇到的模块识别问题。 问题描述 使用 Cursor 或 VSCode 打开 StarRocks 的后端工程 fe 时&#xff0c;spark-…

第五节:基于Winform框架的串口助手小项目---串口收发《C#编程》

“路漫漫其修远兮&#xff0c;吾将上下而求索” &#xff0c; -----------------------WHAPPY 目标任务&#xff1a; 1 从本地设备列表获取串口。 RegistryKey keyCom Registry.LocalMachine.OpenSubKey("Hardware\DeviceMap\SerialComm"); RegistryKey 是.NET 框…

专题二最大连续1的个数|||

1.题目 题目分析&#xff1a; 给一个数字k&#xff0c;可以把数组里的0改成1&#xff0c;但是只能改k次&#xff0c;然后该变得到的数组能找到最长的子串且都是1。 2.算法原理 这里不用真的把0变成1&#xff0c;因为改了比较麻烦&#xff0c;下次用就要改回成1&#xff0c;这…

25年第四本【认知觉醒】

《认知觉醒》&#xff1a;一场与大脑的深度谈判 在信息爆炸的焦虑时代&#xff0c;我们像被抛入湍流的溺水者&#xff0c;拼命抓取各种自我提升的浮木&#xff0c;却在知识的漩涡中越陷越深。这不是一本简单的成功学指南&#xff0c;而是一场关于人类认知系统的深度对话&#…

甘特图开发代码(测试版)

场景&#xff1a;要实现的功能就是单行数据能左右拖动。 流程五个&#xff1a;ABCDE。&#xff08;对应&#xff1a;Charter开发、概念和计划、初样开发、正样开发、验证&#xff09; 1、A有开始时间&#xff0c;结束时间。B的开始时间必须是A的结束时间&#xff08;相等或者…

服务器配置-从0到分析4:ssh免密登入

该部分涉及到公钥、私钥等部分knowledge&#xff0c;本人仅作尝试 若将本地机器 SSH Key 的公钥放到远程主机&#xff0c;就能无需密码直接远程登录远程主机 1&#xff0c;在客户端生成 ssh 公私钥&#xff1a; 也就是我们本地机器&#xff0c;windows电脑 一路回车即可&am…

使用easyocr、PyPDF2对图像及PDF文档进行识别

一、概述 本 Python 脚本的主要功能是对当前目录及其子目录下的图片和 PDF 文件进行光学字符识别&#xff08;OCR&#xff09;处理。它使用 easyocr 库处理图片中的文字&#xff0c;使用 PyPDF2 库提取 PDF 文件中的文本&#xff0c;并将处理结果保存为文本文件。同时&#xff…

2000-2020年各省地方财政一般预算支出数据

2000-2020年各省地方财政一般预算支出数据 1、时间&#xff1a;2000-2020年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标;行政区划代码、地区、年份、地方财政一般预算支出(亿元) 4、范围&#xff1a;31省 5、指标解释&#xff1a;一般预算支出是国家对集中的预算收…

k8s 中各种发布方式介绍以及对比

前言 在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;不同的发布策略&#xff08;如金丝雀发布、灰度发布、蓝绿发布等&#xff09;各有其适用场景和优缺点。 1. 滚动发布&#xff08;Rolling Update&#xff09; 核心原理&#xff1a;逐步替换旧版本 Pod 为新版本&…

力扣HOT100之哈希:1. 两数之和

这道题之前刷代码随想录的时候已经刷过好几遍了&#xff0c;看到就直接秒了。这道题主要是通过unordered_map<int, int>来建立哈希表&#xff0c;其中键用来保存向量中的元素&#xff0c;而对应的值则为元素的下标。遍历整个向量&#xff0c;当遍历到nums[i]时&#xff0…

kakfa-3:ISR机制、HWLEO、生产者、消费者、核心参数负载均衡

1. kafka内核原理 1.1 ISR机制 光是依靠多副本机制能保证Kafka的高可用性&#xff0c;但是能保证数据不丢失吗&#xff1f;不行&#xff0c;因为如果leader宕机&#xff0c;但是leader的数据还没同步到follower上去&#xff0c;此时即使选举了follower作为新的leader&#xff…

从小米汽车召回看智驾“命门”:智能化时代 — 时间就是安全

2025年1月&#xff0c;小米因车辆“授时同步异常”召回3万余辆小米SU7&#xff0c;成为其造车历程中的首个重大安全事件。 从小米SU7召回事件剖析&#xff0c;授时同步何以成为智能驾驶的命门&#xff1f; 2024年11月&#xff0c;多名车主反馈SU7标准版的智能泊车辅助功能出现…

FastGPT 引申:如何基于 LLM 判断知识库的好坏

文章目录 如何基于 LLM 判断知识库的好坏方法概述示例 Prompt声明抽取器 Prompt声明检查器 Prompt 判断机制总结 下面介绍如何基于 LLM 判断知识库的好坏&#xff0c;并展示了如何利用声明抽取器和声明检查器这两个 prompt 构建评价体系。 如何基于 LLM 判断知识库的好坏 在知…