Go-Gin框架

news2024/10/7 4:30:40

一、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 main

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

func main() {
    // 1.创建路由
   r := gin.Default()
   // 2.绑定路由规则,执行的函数
   // gin.Context,封装了request和response
   r.GET("/", func(c *gin.Context) {
      c.String(http.StatusOK, "hello World!")
   })
   // 3.监听端口,默认在8080
   // Run("")里面不指定端口号默认为8080
   r.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.html
	c.Redirect(302, "/index.html")
})
router.GET("/index", func(c *gin.Context) {
	//重定向到/index.html
	c.Redirect(302, "/index.html")
})
router.GET("/index.html", func(c *gin.Context) {
	//返回渲染的html模板中的index.html
	c.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.Method
		c.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 User
	if 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 User

	if 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 User
	if 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
		//接收文件并保存到指定path
		err = 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 routers

import "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 routers

import "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.coloradmin.cn/o/850436.html

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

相关文章

云服务器搭建到使用

云服务器基本搭建 一、搭建二、 配置三、登录 一、搭建 腾讯云为例 1链接进入后选择 国内地域 选择合适的&#xff0c;一般个人学习使用可以选择2核2G就足够了。1核如果需要后面使用就行联合使用可能就比较吃力&#xff0c;比如链接一个vscode来进行g编译。可能体验感就会很差…

网络安全渗透测试之靶场训练

NWES: 7月26号武汉地震检测中心遭受境外具有政府背景的黑客组织和不法分子的网络攻击。 目前网络攻击主要来自以下几种方式: DDOS&#xff1a;分布式拒绝服务攻击。通过制造大量无用的请求向目标服务器发起访问&#xff0c;使其因短时间内无法处理大量请求而陷入瘫痪。主要针对…

在Vue2.0中集成Vform动态表单

目录 一、打包下载源码&#xff0c;引入组件 1、项目地址 2、下载并打包 二、将vform集成到项目中 1、导入v-form-designer、v-form-render两个组件 2、在项目的main.js文件中引入组件&#xff0c;并注册组件 三、注册路由 1、引入VFormDesigner 设计器 2、使用VFor…

Azure Kinect DK + ROS1 Noetic使用教程

作者&#xff1a; Herman Ye Galbot Auromix 版本&#xff1a; V1.0 测试环境&#xff1a; Ubuntu20.04 更新日期&#xff1a; 2023/08/08 注1&#xff1a; 本文内容中的硬件由 Galbot 提供支持。 注2&#xff1a; Auromix 是一个机器人爱好者开源组织。 注3&#xff1a; 本文在…

Ajax同源策略及跨域问题

Ajax同源策略及跨域问题 同源策略ajax跨域问题什么是跨域&#xff1f;为什么不允许跨域&#xff1f;跨域解决方案1、CORS2、express自带的中间件cors3、JSONP原生JSONPjQuery发送JSONP 4、使用vscode的Live Server插件 同源策略 同源策略&#xff08;Same-Origin Policy&#…

Mybatis-plus实现【真·批量插入】

Mybatis基本是现在最为常用的ORM&#xff08;Object Relational Mapping&#xff0c;对象关系映射&#xff09;框架&#xff0c;进行普通的CRUD非常方便。 一. BaseMapper 实体类对应的mapper在继承BaseMapper后&#xff0c;就可以使用以下Mybatis-plus提供的方法进行数据操作…

3个月快速入门LoRa物联网传感器开发

在这里插入图片描述 快速入门LoRa物联网传感器开发 LoRa作为一种LPWAN(低功耗广域网络)无线通信技术,非常适合物联网传感器和行业应用。要快速掌握LoRa开发,需要系统学习理论知识,并通过实际项目积累经验。 摘要: 先学习LoRa基础知识:原理、网络架构、协议等,大概需要2周时间…

高德地图 SDK 接口测试接入(AndroidTest 上手)

学习资料 官方文档 在 Android 平台上测试应用 | Android 开发者 | Android Developers 测试了解 【玩转Test】开篇-Android test 介绍 Android单元测试全解_android 单元测试_一代小强的博客-CSDN博客 Android单元测试-对Activity的测试_activitytestrule_许佳佳233的博客…

SpringMVC的架构有什么优势?——异常处理与文件上传(五)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

PHP8定义字符串的方法-PHP8知识详解

字符串&#xff0c;顾名思义&#xff0c;就是将一堆字符串联在一起。字符串简单的定义方法是使用英文单引号&#xff08; &#xff09;或英文双引号&#xff08;" "&#xff09;包含字符。另外&#xff0c;还可以使用定界符定义字符串。本文还介绍了字符串的连接符。…

脑动极光冲刺上市:盈利能力存疑,永泰生物董事长谭铮为控股股东

8月8日&#xff0c;脑动极光医疗科技有限公司&#xff08;下称“脑动极光”&#xff09;向港交所递交招股书&#xff0c;准备在港交所主板上市&#xff0c;中金公司和浦银国际为其保荐机构。脑动极光在招股书中表示&#xff0c;其是一家根据上市规则18A寻求在港交所上市的生物科…

【80天学习完《深入理解计算机系统》】第二天 2.2 整数的表示【有符号数,无符号数,符号数的扩展,有无符号数的转变】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

重发布选路问题

一、思路 &#xff1b; 1.增加不优选路开销解决选路不佳问题 2.用增加开销的方式使R1 不将ASBR传的R7传给另一台ASBR解决R1、R2、R3、R4pingR7环回环路 二、操作 ------IP地址配置如图 1.ospf及rip的宣告 rip&#xff1a; [r1]rip 1 [r1-rip-1]version 2 [r1-rip-1]netw…

【android】mac mini m2安装android studio

文章目录 一、环境搭建1.1 安装路径1.2 mac arm1.3 安装android studio 二、安装sdk三、更新sdk3.1 关闭代理3.2 重新更新sdk 四、更新api五、项目创建六、gradle安装七、avd八、问题&#xff1a;build tools缺失九、编译运行小结 一、环境搭建 1.1 安装路径 windows&#xf…

HTML5 中新增了哪些表单元素?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ HTML5 中新增了的表单元素⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚…

大数据培训课程-《机器学习从入门到精通》上新啦

《机器学习从入门到精通》课程是一门专业课程&#xff0c;面向人工智能技术服务&#xff0c;课程系统地介绍了Python编程库、分类、回归、无监督学习和模型使用技巧以及算法和案例充分融合。 《机器学习从入门到精通》课程亮点&#xff1a; 课程以任务为导向&#xff0c;逐步学…

linux之find命令

概览 Linux下find命令在目录结构中搜索文件&#xff0c;并执行指定的操作。Linux下find命令提供了相当多的查找条件&#xff0c;功能很强大。由于find具有强大的功能&#xff0c;所以它的选项也很多&#xff0c;其中大部分选项都值得我们花时间来了解一下。即使系统中含有网络…

ESP32 Max30102 (1)检测 心率

1. 运行效果 未检测效果如下 检测效果如下

第九次作业

1. SSL工作过程是什么&#xff1f; 当客户端向一个 https 网站发起请求时&#xff0c;服务器会将 SSL 证书发送给客户端进行校验&#xff0c;SSL 证书中包含一个公钥。校验成功后&#xff0c;客户端会生成一个随机串&#xff0c;并使用受访网站的 SSL 证书公钥进行加密&#xf…

算法基础之插入排序

1、插入排序基本思想 插入排序的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。插入排序在实现上&#xff0c;通常采用in-place排序&#xff08;即只需用到O(1)的额外空间的排序&#xff09;&a…