Gin框架

GoWeb框架

GIN框架

基于httprouter开发的Web框架

安装与使用

安装

下载并安装GIN

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

示例

package mainimport ("github.com/gin-gonic/gin"
)func main() {// 创建一个默认的路由引擎r := gin.Default()// GET:请求方式;/hello:请求的路径// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数r.GET("/hello", func(c *gin.Context) {// c.JSON:返回JSON格式的数据,会将第二个参数中的内容序列化(map和结构体也可以被序列化为json,结构体首字母小写的字段名无法被序列化(无法通过反射赋值))//第一个参数:状态码,第二个参数:被序列化的内容                 c.JSON(200, gin.H{"message": "Hello world!",})})// 启动HTTP服务,默认在0.0.0.0:8080启动服务,也可在参数中制定端口r.Run()
}

将上面的代码保存并编译执行,然后使用浏览器打开127.0.0.1:8080/hello就能看到一串JSON字符串。

restful

get 查

post 增

put 改

delete 删

gin框架支持restful开发

GIN渲染(前端内容)

可用于HTML渲染、自定义模版函数、静态文件处理

获取参数

都是*gin.Context包中的方法,方法的参数都是要获取的请求中的参数的参数名

参数的形式:

1.URL的参数(query string参数)(参数通过?拼接在路径后面)

  • Query() 方法:若参数不存在,返回空串。
  • DefaultQuery() 方法:若参数不存在,返回默认值(默认值可自行设定)
  • GetQuery() 方法:两个返回值,第二个返回值表示是否获取成功

2.表单传参(Form传参)(POST请求)

  • PostForm() 方法
  • DefaultPostForm() 方法:若参数不存在返回默认值(默认值可自行设定)
  • GetPostForm() 方法:两个返回值,第二个返回值表示是否获取成功

3.JSON传参

GetRawData()方法获取json数据,并返回一个字节切片

4.路径参数(path参数)(/user/search/小明/你好)

Param方法()来获取参数

package mainimport ("encoding/json""github.com/gin-gonic/gin""net/http"
)func main() {//创建一个服务ginServer := gin.Default()//连接数据库的代码//访问地址,处理请求 Request ResponseginServer.GET("/hello", func(context *gin.Context) { //使用匿名函数处理请求context.JSON(200, gin.H{"msg": "hello world"}) //返回json,状态码200})//接收前端传递过来的参数//URL传参:/user/info?userid=xxx&username=xxxginServer.GET("/user/info", func(context *gin.Context) {userid := context.Query("userid") //查询参数,用于?后的字符串传参username := context.Query("username")context.JSON(http.StatusOK, gin.H{"userid":   userid,"username": username,})})//路径传参:/user/info/1/xxxginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {userid := context.Param("userid")username := context.Param("username")context.JSON(http.StatusOK, gin.H{"userid":   userid,"username": username,})})//POST方式传递json参数ginServer.POST("/json", func(context *gin.Context) {//获取请求体的原始数据b, _ := context.GetRawData() //b的类型是一个[]byte切片var m map[string]interface{}//将请求体的数据解析为JSON格式(反序列化到m中)_ = json.Unmarshal(b, &m)context.JSON(http.StatusOK, m)})//POST方式传递表单参数ginServer.POST("/user/add", func(context *gin.Context) {username := context.PostForm("username")password := context.PostForm("password")context.JSON(http.StatusOK, gin.H{"msg":      "ok","username": username,"password": password,})})//服务器端口8082ginServer.Run(":8082")
}

参数绑定

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

ShouldBind() 适用于 GET 和 POST 请求,对于 GET 请求,它只使用Form表单绑定引擎(即查询参数)。对于 POST 请求,它会根据 Content-Type 判断是 JSON、XML 还是表单数据,并使用相应的绑定器。

type Login struct {User     string `form:"user" json:"users" binding:"required"`Password string `form:"password" json:"passwords" binding:"required"`
}//form:"user":表示在处理表单数据时,该字段对应表单中的user字段。
//json:"user":表示在进行JSON序列化或反序列化时,该字段对应JSON对象中的user字段。
//binding:"required":表示在进行数据绑定时,该字段是必填项,不能为空。func main() {router := gin.Default()// 绑定JSON的示例 ({"user": "q1mi", "password": "123456"}),用到了json标签router.POST("/loginJSON", func(c *gin.Context) {var login Loginif err := c.ShouldBind(&login); err == nil { //如果没有错误fmt.Printf("login info:%#v\n", login)c.JSON(http.StatusOK, gin.H{"users":     login.User,"passwords": login.Password,})} else { //如果返回了错误,响应400c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 绑定form表单示例 (?user=q1mi&password=123456),用到了form标签router.POST("/loginForm", func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err := c.ShouldBind(&login); err == nil {c.JSON(http.StatusOK, gin.H{"user":     login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 绑定QueryString示例 (/loginQuery?user=q1mi&password=123456),用到了form标签router.GET("/loginForm", func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err := c.ShouldBind(&login); err == nil {c.JSON(http.StatusOK, gin.H{"user":     login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// Listen and serve on 0.0.0.0:8080router.Run(":8080")
}

文件上传

单个文件

func main() {router := gin.Default()// 处理multipart forms提交文件时默认的内存限制是32 MiB// 可以通过下面的方式修改// router.MaxMultipartMemory = 8 << 20  // 8 MiBrouter.POST("/upload", func(c *gin.Context) {// 单个文件file, err := c.FormFile("f1")//从请求中获取携带的参数一样的(请求中的文件名为f1)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error(),})return}log.Println(file.Filename)dst := fmt.Sprintf("C:/tmp/%s", file.Filename)// 上传文件到指定的目录c.SaveUploadedFile(file, dst)//将file上传到dst路径c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("'%s' uploaded!", file.Filename),})})router.Run()
}

多个文件

func main() {router := gin.Default()// 处理multipart forms提交文件时默认的内存限制是32 MiB// 可以通过下面的方式修改// router.MaxMultipartMemory = 8 << 20  // 8 MiBrouter.POST("/upload", func(c *gin.Context) {// 单个文件file, err := c.FormFile("f1")//从请求中获取携带的参数一样的(请求中的文件名为f1)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error(),})return}log.Println(file.Filename)dst := fmt.Sprintf("C:/tmp/%s", file.Filename)// 上传文件到指定的目录c.SaveUploadedFile(file, dst)//将file上传到dst路径c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("'%s' uploaded!", file.Filename),})})router.Run()
}

重定向

http重定向

HTTP 重定向很容易,内部、外部重定向均支持

r.GET("/test", func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")//StatusMovedPermanently重定向状态码301
})

第一个参数:重定向状态码301,第二个参数:重定向到目标网站

路由重定向

路由重定向,使用HandleContext():

r.GET("/test", func(c *gin.Context) {// 指定重定向的URL,访问/test重定向到/test2c.Request.URL.Path = "/test2"//执行后续请求处理,执行/test2的响应r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"hello": "world"})
})

GIN路由

路由

定义端点(URL路径)和处理请求的方法(Handler)之间的映射

普通路由

除了get,put,post等外,还有一个可以匹配所有请求方式的Any方法如下:

r.Any("/test", func(c *gin.Context) {...//可以通过c.Request.Method获取请求方式
})

为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码,下面的代码为没有匹配到路由的请求都返回views/404.html页面

r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, "views/404.html", nil)})

路由组

将拥有共同URL前缀的路由划分为一个路由组,习惯性一对{}包裹同组的路由。(路由组支持嵌套)

func main() {r := gin.Default()userGroup := r.Group("/user"){userGroup.GET("/index", func(c *gin.Context) {...})userGroup.GET("/login", func(c *gin.Context) {...})userGroup.POST("/login", func(c *gin.Context) {...})}shopGroup := r.Group("/shop"){shopGroup.GET("/index", func(c *gin.Context) {...})shopGroup.GET("/cart", func(c *gin.Context) {...})shopGroup.POST("/checkout", func(c *gin.Context) {...})}r.Run()
}

中间件

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

定义中间件

Gin中的中间件必须是一个gin.HandlerFunc类型。

r := gin.Default()

  • 可以在r.GET()等方法中加入中间件,对请求进行拦截
  • 也可以在r.Use()方法中注册全局中间件函数,对全局的所有请求进行拦截

中间件的常用方法

Next()和Abort()

r.Next()

  • 当在中间件中调用Next() 时,Gin框架会继续执行链中的下一个中间件,如果已经没有更多的中间件,就会执行最终的请求处理器函数
  • Next() 通常用于执行前置和后置处理。在前置处理中,你可以修改请求、响应或执行一些检查。在后置处理中,你可以处理响应后的操作,例如记录日志、监控等。
  • 如果在中间件中不调用 Next(),那么后续的中间件和请求处理器将不会被执行

r.Abort()

  • Abort() 用于立即终止中间件链的执行,并且阻止执行任何后续的中间件或请求处理器函数
  • 当调用 Abort() 后,当前中间件可以继续执行余下的代码,但不会执行任何后续的中间件或请求处理器。

Abort() 通常用于以下几种情况:

  1. 需要立即返回错误或特定响应,并且不希望执行任何后续处理。
  2. 已经完成了所有必要的处理,不需要进一步的中间件或处理器介入。
  3. 需要中断请求处理流程,例如重定向到另一个路由。

使用场景对比

  • 前置/后置处理:如果你需要在请求处理前后执行一些代码,应该使用 Next()。
  • 条件处理:如果你根据某些条件决定是否继续执行后续的中间件或处理器,可以在不满足条件时使用 Abort()。
  • 错误处理:如果中间件检测到错误或异常情况,需要立即返回错误响应并终止进一步处理,可以使用 Abort()。

为一个路由注册m1,m2,index三个中间件,m1中用next执行m2中间件,m2中用Abort阻止执行index中间件,随后m2中打印m2 out,返回m1中间件,打印m1 out

Set()和Get()

用于跨中间件存取键值对。只在一次请求中生效,不会影响其它请求

r.Set

在某个中间件中放入一对键值对

r.Get

在其它中间件或最终处理函数中获取放入的键值对

记录接口耗时的中间件

例如我们像下面的代码一样定义一个统计请求耗时的中间件。

// StatCost 是一个统计耗时请求耗时的中间件
func StatCost() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()c.Set("name", "小王子") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值c.Next()// 调用后续的处理函数(后续中间件)// c.Abort() // 阻止调用后续的处理函数(后续中间件)cost := time.Since(start)// 计算后续处理函数的耗时log.Println(cost)}
}
记录响应体的中间件

我们有时候可能会想要记录下某些情况下返回给客户端的响应数据,这个时候就可以编写一个中间件来搞定。


type bodyLogWriter struct {gin.ResponseWriter               // 嵌入gin框架ResponseWriterbody               *bytes.Buffer // 我们记录用的response
}// Write 写入响应体数据
func (w bodyLogWriter) Write(b []byte) (int, error) {w.body.Write(b)                  // 我们记录一份return w.ResponseWriter.Write(b) // 真正写入响应
}// ginBodyLogMiddleware 一个记录返回给客户端响应体的中间件
// https://stackoverflow.com/questions/38501325/how-to-log-response-body-in-gin
func ginBodyLogMiddleware(c *gin.Context) {blw := &bodyLogWriter{body: bytes.NewBuffer([]byte{}), ResponseWriter: c.Writer}c.Writer = blw // 使用我们自定义的类型替换默认的c.Next() // 执行业务逻辑fmt.Println("Response body: " + blw.body.String()) // 事后按需记录返回的响应
}

注册中间件

在gin框架中,我们可以为每个路由添加任意数量的中间件。

为全局路由注册
func main() {// 新建一个没有任何默认中间件的路由r := gin.New()// 注册一个全局中间件r.Use(StatCost())r.GET("/test", func(c *gin.Context) {name := c.MustGet("name").(string) // 从上下文取值log.Println(name)c.JSON(http.StatusOK, gin.H{"message": "Hello world!",})})r.Run()
}
为某个路由单独注册
// 给/test2路由单独注册中间件(可注册多个)r.GET("/test2", StatCost(), func(c *gin.Context) {name := c.MustGet("name").(string) // 从上下文取值log.Println(name)c.JSON(http.StatusOK, gin.H{"message": "Hello world!",})})
为路由组注册中间件

为路由组注册中间件有以下两种写法。

写法1:

shopGroup := r.Group("/shop", StatCost())
{shopGroup.GET("/index", func(c *gin.Context) {...})...
}

写法2:

shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{shopGroup.GET("/index", func(c *gin.Context) {...})...
}

中间件注意事项

gin默认中间件

gin.Default()默认使用了Logger和Recovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

gin中间件中使用goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

运行多个服务

我们可以在多个端口启动服务,例如:

package mainimport ("log""net/http""time""github.com/gin-gonic/gin""golang.org/x/sync/errgroup"
)var (g errgroup.Group
)func router01() http.Handler {e := gin.New()e.Use(gin.Recovery()) //添加一个恢复中间件,用于捕获并处理panic。e.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK,gin.H{"code":  http.StatusOK,"error": "Welcome server 01",},)})return e
}func router02() http.Handler {e := gin.New()e.Use(gin.Recovery()) //添加一个恢复中间件,用于捕获并处理panic。e.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK,gin.H{"code":  http.StatusOK,"error": "Welcome server 02",},)})return e
}func main() {server01 := &http.Server{Addr:         ":8080",Handler:      router01(),ReadTimeout:  5 * time.Second, //设置读取超时时间WriteTimeout: 10 * time.Second, //设置写入超时时间}server02 := &http.Server{Addr:         ":8081",Handler:      router02(),ReadTimeout:  5 * time.Second,WriteTimeout: 10 * time.Second,}// 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务g.Go(func() error {return server01.ListenAndServe() //启动服务器并开始监听请求})g.Go(func() error {return server02.ListenAndServe() //启动服务器并开始监听请求})err := g.Wait(); //等待所有通过 Go 方法启动的任务完成,如果任何一个任务有错误会返回错误if  err != nil {log.Fatal(err) //记录错误}
}

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

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

相关文章

nodejs - nodejs安装步骤

安装 NodeJS 1.下载 NodeJS下载官网&#xff1a;https://nodejs.cn/download/ 2.验证 下载后解压安装&#xff0c;运行如下命令验证安装是否成功&#xff1a; node -v npm -v3.查看默认存放位置 查看npm默认存放位置&#xff0c;运行命令如下&#xff1a; npm get prefix…

Spring Boot框架:计算机课程管理的工程认证之光

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于工程教育认证的计算机课程管理平台的开发全过程。通过分析基于工程教育认证的计算机课程管理平台管理的不足&#xff0c;创建了一个计算机管理基于工程教育认…

游戏设计:推箱子【easyx图形界面/c语言】

在之前写程序设计的大作业时&#xff0c;在哔哩哔哩上跟着一个视频的学习的成果【第一个练习的】 今天整理文件的时候看到的&#xff0c;就发出来一下【CSDN和B站都有详细教程】 不是大项目&#xff0c;只有两个界面 这个代码只有两百行不到&#xff0c;但通过这个把基本的运…

C++数学

前言 C算法与数据结构 打开打包代码的方法兼述单元测试 数论&#xff1a;质数、最大公约数、菲蜀定理 组合数学汇总 计算几何 博弈论 曼哈顿距离与切比雪夫距离 红线是哈曼顿距离&#xff0c;绿线是切比雪夫距离。 二维曼哈顿距离转切比雪夫距离 曼哈顿距离&#xff1a;|…

如何安装VMWare Workstation 16虚拟机

1、到VMware官网下载安装包。 2、下一步。 3、勾选同意协议&#xff0c;下一步。 4、更换安装路径&#xff0c;下一步。 5、取消全部勾选&#xff0c;下一步。 6、下一步。 7、安装。 8、等待安装完成。 9、安装完成&#xff0c;启动软件。 10、输入许可证ZF3R0…

光流分析技术

光流分析技术是一种重要的计算机视觉和图像处理技术&#xff0c;它通过分析连续帧图像中像素点的运动轨迹和速度&#xff0c;来捕捉图像中物体的运动和相邻帧之间的位移信息。以下是对光流分析技术的详细介绍&#xff1a; 一、光流的基本概念 光流&#xff08;Optical Flow&am…

Bearer 和 Digest 两个区别

Bearer 和 Digest 是两种常见的身份验证机制,主要用于在网络通信中验证用户的身份,以下是它们之间的区别: 认证原理 Bearer:也称为承载令牌认证,其核心是使用一个令牌(Token)来代表用户的身份信息。用户在进行身份验证后,服务器会颁发一个令牌给客户端,客户端在后续…

H264三种RTP打包方式

1. 单一NALU模式 单一NALU模式 适用于小于MTU&#xff08;最大传输单元&#xff09;的NALU。这种模式下&#xff0c;一个RTP包包含一个完整的NALU。RTP头部之后紧跟着NALU头和NALU数据。 封装格式&#xff1a; RTP头 | NALU头 | NALU数据这种方式简单直接&#xff0c;但仅适…

亚马逊评论爬虫+数据分析

爬取评论 做分析首先得有数据&#xff0c;数据是核心&#xff0c;而且要准确&#xff01; 1、爬虫必要步骤&#xff0c;选好框架 2、开发所需数据 3、最后测试流程 这里我所选框架是seleniumrequest&#xff0c;很多人觉得selenium慢&#xff0c;确实不快&#xff0c;仅针对此…

批量缓存模版

批量缓存模版 缓存通常有两种使用方式&#xff0c;一种是Cache-Aside&#xff0c;一种是cache-through。也就是旁路缓存和缓存即数据源。 一般一种用于读&#xff0c;另一种用于读写。参考后台服务架构高性能设计之道。 最典型的Cache-Aside的样例&#xff1a; //读操作 da…

09 Oracle数据拯救:Flashback Technologies精细级数据恢复指南

文章目录 09 Oracle数据拯救&#xff1a;Flashback Technologies精细级数据恢复指南一、Flashback Technologies概览二、Flashback Query&#xff1a;查询过去的数据三、Flashback Table&#xff1a;恢复整个表四、Flashback Database&#xff1a;恢复整个数据库五、总结与最佳…

BIST(Built-in Self-Test,内建自测试)学习笔记

参考资料: 内建自测试&#xff08;Built-in Self-Test&#xff0c;简称BIST&#xff09;详解_built in self test-CSDN博客 芯片测试术语 &#xff0c;片内测试(BIST)&#xff0c;ATE测试-CSDN博客 可能是DFT最全面的介绍--BIST - 知乎 (zhihu.com) 汽车功能安全--TC3xx LB…

three.js 杂记

在Three.js中&#xff0c;Object3D是所有3D对象的基类&#xff0c;而Group是Object3D的一个子类。Group的目的是为了简化处理多个对象的集合。当你将对象添加到Group中时&#xff0c;它们会以一个单元格的形式被处理&#xff0c;参与Group的某些操作&#xff0c;例如位置更新、…

go函数传值是值传递?还是引用传递?slice案例加图解

先说下结论 Go语言中所有的传参都是值传递&#xff08;传值&#xff09;&#xff0c;都是一个副本&#xff0c;一个拷贝。 值语义类型&#xff1a;参数传递的时候&#xff0c;就是值拷贝&#xff0c;这样就在函数中就无法修改原内容数据。 基本类型&#xff1a;byte、int、bool…

穿越时空的全球时钟:一个实时多时区显示的网页应用

引言 在当今这个全球化时代&#xff0c;人们经常需要与世界各地的朋友、同事或客户进行沟通。然而&#xff0c;由于时差的存在&#xff0c;找到一个合适的沟通时间往往成为一大挑战。为了解决这一问题&#xff0c;我们开发了一个名为“全球时钟”的网页应用&#xff0c;它能够…

本地部署免费开源助手Ollama

Ollama 安装 安装ollama 官方网站&#xff1a;https://ollama.com/download 2. 安装成功 3. 运行模型 模型&#xff1a;https://ollama.com/library 运行&#xff1a; ollama run llama3.2:3b Mac 、Linux 版本安装类似。 Open-WebUI界面安装 openwebui官网&#xff1a;http…

three.js杂记

空间 - 位置变换&#xff1a; // 假设有一个Three.js的对象: object3D // 存储矩阵位置 const matrix object3D.matrix.clone(); const matrixArray matrix.toArray(); // 转换为数组 // 之后&#xff0c;当你需要恢复位置时 object3D.matrix.fromArray(matrixArray); …

通过DNS服务器架构解释DNS请求过程

在前面的章节,这里,基于PCAP数据包和RFC文档详细介绍了DNS请求和响应的每个字段的含义。但是在现实的网络世界中,DNS请求和响应的数据包是怎么流动的,会经过哪些设备。本文将着重说明一下目前网络空间中DNS请求和响应的流动过程。 当前网络空间中比较常见DNS请求的流程如下…

HBase使用create创建表时报错ERROR: KeeperErrorCode = NoNode for /hbase/master

场景模拟 1. 正常情况 模拟ERROR: KeeperErrorCode NoNode for /hbase/master错误场景。 正常情况下创建hbase表如下图所示。 2. 删除hbase集群的zk节点 进入zookeeper客户端。 zkCli.sh删除hbase的zk节点。 deleteall /hbase退出zookeeper客户端。 quit3. 重启hbase集…

软件分享丨火绒应用商店

【资源分享】 资源名&#xff1a;火绒应用商店 官方网址&#xff1a;点击跳转 火绒应用商店是由火绒安全推出的一款独立软件。它提供了海量的应用程序&#xff0c;涵盖办公、社交、游戏、视频、工具等多种领域和类别&#xff0c;方便用户轻松找到所需的应用并进行一键下载安装…