Go-Gin框架

一、Gin介绍

Gin是一个用Go编写的HTTPweb框架。它是一个类似于martini但拥有更好性能的API框架, 优于httprouter,速度提高了近 40 倍。 点击此处访问Gin官方中文文档。

二、安装

1、安装Gin

go get -u github.com/gin-gonic/gin

2、代码中引入

import "github.com/gin-gonic/gin"

3、简单示例,验证

新建文件main.go,内容如下:

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

运行后访问: http://localhost:8000/

三、渲染前端与配置跨域


1、渲染html模板


Gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据,本质上就是字符串替换。LoadHTMLGlob()方法可以加载模板文件(参数为待渲染的html模板文件,如果参数为相对路径则为运行路径的相对路径)。

a、 渲染单个文件

r.LoadHTMLGlob("web/index.html")


b、 渲染文件夹下的所有文件

r.LoadHTMLGlob("web/*")


c、 渲染文件夹下的所有html后缀的文件
比如:

r.LoadHTMLGlob("web/*.html")



2、定义模板分割

r.Delims("<<<", ">>>")

第一个参数为:模板标签开始标记
第二个参数为:模板标签结束标记

3、渲染静态文件和目录
如果你需要引入静态文件需要定义一个静态文件目录

r.Static("/assets", "./assets")


若assets的目录结构为


可以根据 http://localhost:8000/assets/img/home.jpg 访问指定资源

 如果r为路由组,则需要在assets前拼接路由组的路径包括其前缀

4、重定向

// 重定向两种默认应支持的首页访问方式
router.GET("/", func(c *gin.Context) {//重定向到/index.htmlc.Redirect(302, "/index.html")
})
router.GET("/index", func(c *gin.Context) {//重定向到/index.htmlc.Redirect(302, "/index.html")
})
router.GET("/index.html", func(c *gin.Context) {//返回渲染的html模板中的index.htmlc.HTML(http.StatusOK, "index.html", gin.H{"baseUrl": "http://" + host,})
})

5、配置跨域

Next()

r.Use(Next())

允许跨域

// 允许跨域
func Next() gin.HandlerFunc {return func(c *gin.Context) {method := c.Request.Methodc.Header("Access-Control-Allow-Origin", "*")c.Header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers,Authorization,User-Agent, Keep-Alive, Content-Type, X-Requested-With,X-CSRF-Token,AccessToken,Token")c.Header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, OPTIONS")c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")c.Header("Access-Control-Allow-Credentials", "true")// 放行所有OPTIONS方法if method == "OPTIONS" {c.AbortWithStatus(http.StatusAccepted)}c.Next()}
}

四、路由相关

1、创建路由组

// 创建路由组
// 根据需要,可以为这种多级的路由组:r.Group("/v1/user")
userApi:= r.Group("/user")// 创建用户
// 匹配POST请求的 /user
userApi.POST("", userCreate)// 修改用户
// 匹配PUT请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.PUT("/:id", userUpdate)// 获取用户
// 匹配GET请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.GET("/:id", userGet)// 查询用户
// 匹配GET请求的 /user/list
userApi.GET("/list", userQuery)// 删除用户
// 匹配DELETE请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.DELETE("/:id", userDelete)

Restful风格的API

2、获取路由参数

a、api参数

通过Context的Param方法来获取api参数
 

userApi.GET("/:id/:name", userGet)

第一个参数:获取url路径参数id和name的值

第二个参数:userGet函数

func userGet(ctx *gin.Context) {//api参数可以为单个或者多个也可以拼接多级//ctx.Param()函数获取时参数需要与api中的名称一致才能获取到id := ctx.Param("id")name:= ctx.Param("name")ctx.JSON(http.StatusOK, gin.H{"data": id,"name":name})return
}

http://localhost:8000/user/1/admin

b、url参数

通过Context的Query方法与DefaultQuery方法来获取url参数

userApi.GET("/list", userQuery)

userQuery方法

func userQuery(ctx *gin.Context) {//获取id := ctx.Query("id")//获取,第二个参数为获取为空的默认值,如果参数不存在则放回第二个参数name := ctx.DefaultQuery("name", "user")ctx.JSON(http.StatusOK, gin.H{"data": id, "name": name})return
}

备注:默认值为当客户端没有在请求中带这个参数,服务端将取name默认值为”user“

可以根据 http://localhost:8000/user/list 后面拼接查询参数,访问此接口

 客户端没有有传参数,则取默认值

 客户端有传参数,则不取默认值

c、表单参数


表单参数测试与观察请求效果需要安装postman
通过Context的PostForm方法来获取表单参数

userApi.POST("", userCreate)

userCreate函数为

func userCreate(ctx *gin.Context) {id := ctx.PostForm("id")name := ctx.PostForm("name")ctx.JSON(http.StatusOK, gin.H{"data": id, "name": name})return
}



如图,访问此接口

d、json参数

json参数测试与观察请求效果需要安装postman
通过Context的GetRawData或者ShouldBindJSON方法来获取表单参数

userApi.PUT("/:id", userUpdate)

userUpdate函数为


GetRawData方法

func userUpdate(ctx *gin.Context) {b, err := ctx.GetRawData() // 从ctx.Request.Body读取请求数据if err !=nil{fmt.print(err)}// 定义map或结构体var m map[string]interface{}// 反序列化_ = json.Unmarshal(b, &m)ctx.JSON(http.StatusOK, gin.H{"data": m["id"], "name": m["name"]})return
}

ShouldBindJSON方法

// 先定义结构
type User struct {Id   string `form:"id" json:"id" binding:"required"`Name string `form:"name" json:"name" binding:"required"`
}// 函数实现
func userUpdate(ctx *gin.Context) {var user Userif err := ctx.ShouldBindJSON(&user); err == nil {ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})return} else {ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})return}
}

e、参数绑定

为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中form表单、JSON、XML等参数到结构体中。


代码示例:


定义要获取的结构

type User struct {Id   string `form:"id" json:"id" binding:"required"`Name string `form:"name" json:"name" binding:"required"`
}

userCreate函数

func userCreate(ctx *gin.Context) {// 实例化结构体对象var user Userif err := ctx.ShouldBind(&user); err == nil {// 响应体 json 格式ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})return}else{ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})return}
}

userUpdate函数

func userUpdate(ctx *gin.Context) {// 实例化结构体对象var user Userif err := ctx.ShouldBind(&user); err == nil {ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})return}else{ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})return}
}

3、上传文件

一般都是post请求表单传参

userApi.POST("/upload", userUpload)

userUpload函数

// 先定义结构
type FileUpload struct {File *multipart.FileHeader `form:"file"`Type string                `form:"type"`
}
// 函数实现
func userUpload(ctx *gin.Context) {var fileUpload FileUpload
// if err := ctx.ShouldBind(&fileUpload); err == nil {//获取运行路径ex, err := os.Executable()//if err != nil {ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})return}//定义接收文件的存放地址path := filepath.Dir(ex) + string(os.PathSeparator) + fileUpload.File.Filename//接收文件并保存到指定patherr = ctx.SaveUploadedFile(fileUpload.File, path)ctx.JSON(http.StatusOK, gin.H{"data": path, "type": fileUpload.Type})return} else {ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})return}
}

五、路由分组分文件

使用Gin框架写Go项目的时候,如果把路由都写在一个.go文件中,随着路由的数量的增加,会导致路由文件显得异常臃肿。

此时需要对路由进行分组并且分文件管理,更有利于代码的组织和维护

使用Gin框架写Go项目的时候,如果把路由都写在一个.go文件中,随着路由的数量的增加,会导致路由文件显得异常臃肿。

此时需要对路由进行分组并且分文件管理,更有利于代码的组织和维护

路由分组的实现

原始未分组的文件

func main() {router := gin.Default()router.POST("/one/a", OneAFunc)router.POST("/one/b", OneBFunc)router.POST("/one/c", OneCFunc)router.POST("/one/d", OneDFunc)router.POST("/one/e", OneEFunc)router.POST("/one/f", OneFFunc)router.POST("/one/g", OneGFunc)router.POST("/one/h", OneHFunc)router.POST("/one/i", OneIFunc)/*省略n条路由*/router.POST("/one/x", OneXFunc)router.POST("/one/y", OneYFunc)router.POST("/one/z", OneZFunc)router.POST("/two/a", TwoAFunc)router.POST("/two/b", TwoBFunc)router.POST("/two/c", TwoCFunc)router.POST("/two/d", TwoDFunc)router.POST("/two/e", TwoEFunc)router.POST("/two/f", TwoFFunc)router.POST("/two/g", TwoGFunc)router.POST("/two/h", TwoHFunc)router.POST("/two/i", TwoIFunc)/*省略n条路由*/router.POST("/two/x", TwoXFunc)router.POST("/two/y", TwoYFunc)router.POST("/two/z", TwoZFunc)router.Run(":8080")
}

路由逻辑分组

我们首先创建了一个路由组 one,它的前缀为 /one。

然后,使用了 POST 方法在路由组 one 中定义了多个路由处理函数 oneAFunc到oneZFunc,它们分别处理 /two/a到 /two/z 路由的 HTTP POST 请求

请求路径

/one/a/

/tow/a/

	// 路由分组第一组one := router.Group("/one"){one.POST("/a", OneAFunc)one.POST("/b", OneBFunc)one.POST("/c", OneCFunc)one.POST("/d", OneDFunc)one.POST("/e", OneEFunc)one.POST("/f", OneFFunc)one.POST("/g", OneGFunc)one.POST("/h", OneHFunc)one.POST("/i", OneIFunc)/*省略n条路由*/one.POST("/x", OneXFunc)one.POST("/y", OneYFunc)one.POST("/z", OneZFunc)}//路由分组第二组two := router.Group("/two"){two.POST("/a", twoAFunc)two.POST("/b", twoBFunc)two.POST("/c", twoCFunc)two.POST("/d", twoDFunc)two.POST("/e", twoEFunc)two.POST("/f", twoFFunc)two.POST("/g", twoGFunc)two.POST("/h", twoHFunc)two.POST("/i", twoIFunc)/*省略n条路由*/two.POST("/x", twoXFunc)two.POST("/y", twoYFunc)two.POST("/z", twoZFunc)}

分组后的go代码,虽然路由都实现路由分组,路由的逻辑显得清晰,但是路由文件还是集中在一个文件中,文件还是大。

路由分组后并分文件管理

文件结构

main.go
handlers/
├── one_handlers.go
└── two_handlers.go

main.go

func main() {router := gin.Default()// 路由分组第一组routers.TwoRoutersInit(router)//路由分组第二组routers.OneRoutersInit(router)router.Run(":8080")
}

TwoRoutersInit() 和 OneRoutersInit()对应下面两个文件:

one_handlers.go

package routersimport "github.com/gin-gonic/gin"func OneRoutersInit(engine *gin.Engine) {one := engine.Group("/one"){one.POST("/a", OneAFunc)one.POST("/b", OneBFunc)one.POST("/c", OneCFunc)one.POST("/d", OneDFunc)one.POST("/e", OneEFunc)one.POST("/f", OneFFunc)/*省略n条路由*/one.POST("/x", OneXFunc)one.POST("/y", OneYFunc)one.POST("/z", OneZFunc)}
}func OneAFunc(context *gin.Context) {}
/*省略N多方法
*/
func OneZFunc(context *gin.Context) {}

two_handlers.go

package routersimport "github.com/gin-gonic/gin"func TwoRoutersInit(engine *gin.Engine) {two := engine.Group("/two"){two.POST("/a", twoAFunc)two.POST("/b", twoBFunc)two.POST("/c", twoCFunc)two.POST("/d", twoDFunc)two.POST("/e", twoEFunc)two.POST("/f", twoFFunc)two.POST("/g", twoGFunc)two.POST("/h", twoHFunc)two.POST("/i", twoIFunc)/*省略n条路由*/two.POST("/x", twoXFunc)two.POST("/y", twoYFunc)two.POST("/z", twoZFunc)}
}func twoAFunc(context *gin.Context) {}
/*
省略n多方法
*/func twoZFunc(context *gin.Context) {}

备注:每个路由都要放在{}中,每个路由对应的方法入参固定context *gin.Context  其中context风状态request和response

六、中间件

1、统一注册中间件

对全局路由或者已注册的路由组统一注册中间件

r.Use(CheckToken())

2、单独注册中间件

userApi.POST("/upload",CheckToken(),userUpload)

3、中间件函数实现

func CheckToken() gin.HandlerFunc {return func(c *gin.Context) {// 验证不通过直接跳出//c.JSON(http.StatusBadRequest, gin.H{"msg": "need token"})//c.Abort()//return// 验证通过继续c.Next()}
}

中间件场景

Go Gin JWT

https://blog.csdn.net/u013302168/article/details/132178429

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

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

相关文章

安路FPGA的赋值报错——移位处理,加括号

authordaisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 在使用移位符号用来当作除以号使用时&#xff0c;发现如下问题 其中 cnt_8K 为偶数和奇数时输出的数据不一样 reg [10:0] cnt_8K; reg [10:0] ram1_addra; always(posedge clk_16M) begin if(ram_out_flag )begin if(…

【flink】Checkpoint expired before completing.

使用flink同步数据出现错误Checkpoint expired before completing. 11:32:34,455 WARN org.apache.flink.runtime.checkpoint.CheckpointFailureManager [Checkpoint Timer] - Failed to trigger or complete checkpoint 4 for job 1b1d41031ea45d15bdb3324004c2d749. (2 con…

换架 3D 飞机,继续飞呀飞

相信大多数图扑 HT 用户都曾见过这个飞机的 Demo&#xff0c;在图扑发展的这十年&#xff0c;这个 Demo 是许多学习 HT 用户一定会参考的经典 Demo 之一。 这个 Demo 用简洁的代码生动地展示了 OBJ 模型加载、数据绑定、动画和漫游等功能的实现。许多用户参考这个简单的 Demo 后…

kubernetes 集群命令行工具 kubectl

1、kubectl 概述 kubectl是一种命令行工具&#xff0c;用于管理Kubernetes集群和与其相关的资源。通过kubectl&#xff0c;您可以查看和管理Pod、Deployment、Service、Volume、ConfigMap等资源&#xff0c;也可以创建、删除和更新它们。 kubectl还提供了许多其他功能&#x…

中国信通院腾讯安全发布《2023数据安全治理与实践白皮书》

导读 腾讯科技(深圳)有限公司和中国信息通信研究院云计算与大数据研究所共同编制了本报告。本报告提出了覆盖组织保障、管理流程、技术体系的以风险为核心的数据安全治理体系&#xff0c;并选取了云场景、互娱、社交等场景&#xff0c;介绍相应场景下数据安全治理实践路线及主…

【Vue】使用print.js插件实现打印预览功能,超简单

目录 一、实现效果 二、实现步骤 【1】安装插件 【2】在需要打印的页面导入 【3】在vue文件中需要打印的部分外层套一层div&#xff0c;给div设置id。作为打印的区域 【4】在打印按钮上添加打印事件 【5】在methods中添加点击事件 三、完整代码 一、实现效果 二、实现步…

16-2_Qt 5.9 C++开发指南_使用样式表Qss自定义界面

进行本篇介绍学习前&#xff0c;请先参考链接01_1_Qt工程实践_Qt样式表Qss&#xff0c;后再结合本篇进行融合学习如何使用样式表定义界面。 文章目录 1. Qt样式表2. Qt样式表句法2.1 一般句法格式2.2 选择器 (selector)2.3 子控件&#xff08;sub-controls&#xff09;2.4 伪状…

构建Docker容器监控系统(cadvisor+influxDB+grafana)

目录 一、部署 1、安装docker-cd 2、阿里云镜像加速 3、下载组件镜像 4、创建自定义网络 5、创建influxdb容器 6、创建Cadvisor 容器 7、创建granafa容器 一、部署 1、安装docker-cd [rootlocalhost ~]# iptables -F [rootlocalhost ~]# setenforce 0 setenforce: SELi…

RS485实验

RS485实验 介绍 RS485采用差分信号进行传输&#xff0c;半双工通信。RS485是一个总线&#xff0c;在同一总线上最多可以挂接32个节点。通信流程简单理解为默认为接收状态&#xff0c;发送数据时切换为发送状态&#xff0c;数据发送完毕后切换为接收状态。发送和接收分别由一个…

[考研机试] KY20 完数VS盈数 清华大学复试上机题 C++实现

描述 一个数如果恰好等于它的各因子(该数本身除外)子和&#xff0c;如&#xff1a;6321。则称其为“完数”&#xff1b;若因子之和大于该数&#xff0c;则称其为“盈数”。 求出2到60之间所有“完数”和“盈数”。 输入描述&#xff1a; 题目没有任何输入。 输出描述&#…

MachineLearningWu_13/P60-P64_Tensorflow

P60-P64的学习目录如下&#xff0c; x.1 TF网络模型实现 以一个简单的TF的分类网络为例&#xff0c;将模型翻译成框架下的语义&#xff0c;即如右侧所表达的。 当然上面对于分类网络的解释是一个简洁的解释&#xff0c;我们来进行更加具象的了解一下。左边是机器学习的三步骤&…

LeetCode题解:判断是否能拆分数组

⭐简单说两句⭐ 作者&#xff1a;后端小知识 CSDN个人主页&#xff1a;后端小知识 &#x1f50e;GZH&#xff1a;后端小知识 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 上周做了力扣周赛的题&#xff0c;给大家分享一个中等题目&#xff…

文章采集伪原创发布工具-147采集

在当今信息爆炸的时代&#xff0c;企业和个人都意识到了获取高质量、原创的内容的重要性。然而&#xff0c;手动撰写大量的原创内容是一项耗时费力的任务。为了解决这个问题&#xff0c;我向您介绍一款颠覆性的数据采集工具——147采集。 147采集是一款专业且高效的数据采集软件…

Linux安装配置nginx+php搭建以及在docker中配置

Linux安装配置nginxphp搭建以及在docker中配置 文章目录 Linux安装配置nginxphp搭建以及在docker中配置1.nginx源码包编译环境和安装相应的依赖1.1 安装编译环境1.2 安装pcre库、zlib库和openssl库 2.安装nginx2.1 在[nginx官网](https://nginx.org/en/download.html)上获取源码…

IDEA 指定spring.profiles.active本地启动

spring.profiles.activedev spring.profiles.activepro

Android开发实践:Android.mk模板

关于Android NDK开发的文章已经比较多了&#xff0c;我的博客中也分享了很多NDK开发相关经验和技巧&#xff0c;今天简单写了一个 Android.mk 的示例模板&#xff0c;供初学者参考。 本模板主要给大家示例 Android NDK 开发中的如下几个问题&#xff1a; 如何自动添加需要编译…

15_基于Flink将pulsar数据写入到ClickHouse

3.8.基于Flink将数据写入到ClickHouse 编写Flink完成数据写入到ClickHouse操作, 后续基于CK完成指标统计操作 3.8.1.ClickHouse基本介绍 ClickHouse 是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;主要用…

K最近邻算法:简单高效的分类和回归方法(三)

文章目录 &#x1f340;引言&#x1f340;训练集和测试集&#x1f340;sklearn中封装好的train_test_split&#x1f340;超参数 &#x1f340;引言 本节以KNN算法为主&#xff0c;简单介绍一下训练集和测试集、超参数 &#x1f340;训练集和测试集 训练集和测试集是机器学习和深…

【LeetCode】数据结构题解(11)[用队列实现栈]

用队列实现栈 &#x1f609; 1.题目来源&#x1f440;2.题目描述&#x1f914;3.解题思路&#x1f973;4.代码展示 所属专栏&#xff1a;玩转数据结构题型❤️ &#x1f680; >博主首页&#xff1a;初阳785❤️ &#x1f680; >代码托管&#xff1a;chuyang785❤️ &…

C语言文件操作基本方法

1、文件的分类 ANSI C 的缓冲文件系统 缓冲文件系统 缓冲文件系统是指&#xff0c;系统自动地在内存区为每个正在使用的文件开辟一个缓冲区。 从内存向磁盘输出数据时&#xff0c;必须首先输出到缓冲区中。待缓冲区装满后&#xff0c;再一起输出到磁盘文件中。 从磁盘文件向内…