【Gin框架深度解析】路由实现原理,让你彻底掌握Gin中路由的奥秘!

Gin路由

1、基本路由

​ 举一个例子:

package main

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

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "hello word")
    })
    r.POST("/xxxpost",getting)
    r.PUT("/xxxput")
    //监听端口默认为8080
    r.Run(":8000")
}

//----------------解析-------------------//
	这里我们不做过多解释整个代码,在Gin简介中就有包含对代码的解释,主要是来说明下:r.POST()和r.PUT()方法。r.POST()和r.PUT()都是路由函数,用于绑定指定的HTTP请求方法(POST和PUT)和对应的处理函数。

如r.POST()用于绑定HTTP的POST请求,r.PUT()用于绑定HTTP的PUT请求。这两个函数的第一个参数是请求路径,第二个参数是处理该请求的函数。例如,在你的代码中,r.POST("/xxxpost", getting)表示将HTTP的POST请求与名为getting的函数绑定到/xxxpost路径上。

这样,在客户端发起POST请求到/xxxpost路径时,服务器将会调用名为getting的处理函数来处理该请求。同理,当客户端发起PUT请求到指定的路径时,服务器将会调用相应的处理函数来处理请求。

2、Restful风格的API

​ gin是支持restful风格的api(Representational State Transfer)-> 表现层状态转化,这是互联网应用程序的API设计理念:URL定位资源,用HTTP描述操作

1.获取文章 /blog/getXxx Get blog/Xxx

2.添加文章 /blog/addXxx POST blog/Xxx

3.修改文章 /blog/updateXxx PUT blog/Xxx

4.删除文章 /blog/delXxxx DELETE blog/Xxx

//----------------解析-------------------//
这段描述是一个 RESTful API 中对博客文章的四个基本操作的接口规范,其中 GET、POST、PUT 和 DELETE 都是 HTTP 协议中的请求方法,分别表示获取资源、新增资源、修改资源和删除资源。具体对应到博客文章上的操作可以描述如下:

获取文章:通过 GET 请求获取指定博客文章的内容,如 /blog/getXxx 可以获取 ID 为 Xxx 的博客文章的详细内容。

添加文章:通过 POST 请求向服务器提交一篇新的博客文章内容,如 /blog/addXxx 可以将新的博客文章添加到服务器上,并返回添加后的文章的信息。

修改文章:通过 PUT 请求更新指定博客文章的内容,如 /blog/updateXxx 可以更新 ID 为 Xxx 的博客文章的信息,可以修改博客文章的标题、内容、标签等属性。

删除文章:通过 DELETE 请求删除指定博客文章,如 /blog/delXxx 可以删除 ID 为 Xxx 的博客文章。在实际应用中,为了避免误删,通常会要求用户再次确认删除操作。

3、API参数

1、可以通过Context的Param方法来获取API参数

2、localhost:8000/xxx/zhangsan

package main

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

func main() {
	r := gin.Default()
	r.GET("/user/:name/*action", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		// 截取
		action = strings.Trim(action, "/")
		c.String(http.StatusOK, name+" is "+action)
	})

	// 默认监听端口为8080
	r.Run(":8000")
}

//----------------解析-------------------//
这里需要注意几个点:
1:name 和 *action -> 在gin框架中,使用冒号(:)来定义路径参数,使用星号(*)来定义匹配路径参数中斜杠(/)的通配符。例如,:name可以匹配任意非斜杠的字符,而*action可以匹配任意字符串,包括斜杠。这些符号不是固定写法,但是是gin框架中的约定俗成的写法。
2string.Trim方法:是 Go 语言中一个字符串处理的函数,用于去除字符串中开头和结尾的指定字符,返回处理后的字符串。函数签名为:Trim(s string, cutset string) string。其中 s 代表需要处理的字符串,cutset 代表需要去除的字符集合。在这段代码中,strings.Trim(action, "/"),表示除去action头尾的`/`

3、输出结果

4、URL参数

​ 有了API参数,当然也有URL参数,其中URL参数是通过DefaultQuery()或Query()方法获取的

  • 1)DefaultQuery()若参数不存在,则返回默认值;2)Query()若不存在,则返回空串。
  • API?name=xxx
package main

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

func main() {
	r := gin.Default()
	r.GET("/user", func(c *gin.Context) {
		// 制定默认值
		// http://localhost:8080/user 才会打印出默认的值
		name := c.DefaultQuery("name", "访问到初始路径,返回默认值!")
		c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
	})
	r.Run(":8000")
}

1、返回默认值

2、返回非默认值

5、表单参数

​ 表单传输为post请求,http常见的传输格式为4中:

  • application/json
  • application/x-www-form-urlencoded
  • application/xml
  • multipart/form-data

1、代码

package main

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

func main() {
	r := gin.Default()
	r.POST("/form", func(c *gin.Context) {
		types := c.DefaultPostForm("type", "post")
		username := c.PostForm("username")
		password := c.PostForm("userpassword")
		c.String(http.StatusOK, fmt.Sprintf("username : %s, password : %s, type : %s", username, password, types))
	})
	r.Run()
}

//----------------解析-------------------//
这段代码创建了一个基于gin框架的Web服务器,当收到一个POST请求时,解析请求中的表单参数,并返回包含这些参数的字符串。

具体地说,这个Web服务器会监听地址为:8080的HTTP请求。当收到一个POST请求时,会调用回调函数func(c *gin.Context),在其中通过c.DefaultPostForm和c.PostForm获取请求中的表单参数。其中,c.DefaultPostForm可以获取表单参数中的指定参数,如果该参数不存在,则使用指定的默认值。而c.PostForm可以获取表单参数中的指定参数,如果该参数不存在则返回空字符串。最后,服务器会返回一个字符串,其中包含了解析到的表单参数的信息。

2、输出结果

6、上传单个文件

​ multipart/form-data格式用于文件的上传;gin文件上传与原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中

1、html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
          上传文件:<input type="file" name="file" >
          <input type="submit" value="提交">
    </form>
</body>
</html>

2、go文件

package main

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

func main() {
    r := gin.Default()
    //限制上传最大尺寸
    r.MaxMultipartMemory = 8 << 20
    r.POST("/upload", func(c *gin.Context) {
        file, err := c.FormFile("file")
        if err != nil {
            c.String(500, "上传图片出错")
        }
        // c.JSON(200, gin.H{"message": file.Header.Context})
        c.SaveUploadedFile(file, file.Filename)
        c.String(http.StatusOK, file.Filename)
    })
    r.Run()
}

3、效果演示

6.1、上传特定文件

​ 有时候用户文件上传的时候需要指定文件的类型或者上传文件的大小,但是gin框架暂时没有提供这些函数,因此基于原生的函数写法自己创造一个可以限制文件大小及类型的函数。

package main

import (
    "fmt"
    "log"
    "net/http"

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

func main() {
    r := gin.Default()
    r.POST("/upload", func(c *gin.Context) {
        _, headers, err := c.Request.FormFile("file")
        if err != nil {
            log.Printf("Error when try to get file: %v", err)
        }
        //headers.Size 获取文件大小
        if headers.Size > 1024*1024*2 {
            fmt.Println("文件太大了")
            return
        }
        //headers.Header.Get("Content-Type")获取上传文件的类型
        if headers.Header.Get("Content-Type") != "image/png" {
            fmt.Println("只允许上传png图片")
            return
        }
        c.SaveUploadedFile(headers, "./video/"+headers.Filename)
        c.String(http.StatusOK, headers.Filename)
    })
    r.Run()
}

//----------------解析-------------------//
这段代码使用 Gin 框架实现了一个简单的文件上传功能。在这个程序中,我们使用了 POST 请求来上传文件,URL 为 "/upload"。

在路由处理函数中,通过 c.Request.FormFile 方法获取文件对象,文件对象类型为 *multipart.FileHeader。在获取到文件对象之后,我们可以使用 FileHeader.Size 属性获取文件大小,使用 FileHeader.Header.Get("Content-Type") 获取文件类型。在这个程序中,我们限制上传文件的大小不能超过 2MB,且只允许上传 png 图片。

当文件通过验证后,使用 c.SaveUploadedFile 方法将文件保存在本地。最后,使用 c.String 方法返回上传文件的文件名。

需要注意的是,在程序运行之前,需要先确保项目目录下存在 video 目录。

7、上传多个文件

1、html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
          上传文件:<input type="file" name="files" multiple>
          <input type="submit" value="提交">
    </form>
</body>
</html>

2、go文件

package main

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

// gin的helloWorld

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // 限制表单上传大小 8MB,默认为32MB
   r.MaxMultipartMemory = 8 << 20
   r.POST("/upload", func(c *gin.Context) {
      form, err := c.MultipartForm()
      if err != nil {
         c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
      }
      // 获取所有图片
      files := form.File["files"]
      // 遍历所有图片
      for _, file := range files {
         // 逐个存
         if err := c.SaveUploadedFile(file, file.Filename); err != nil {
            c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
            return
         }
      }
      c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
   })
   //默认端口号是8080
   r.Run(":8000")
}

8、路由分组

​ 路由分组(router groups)是为了管理一些相同的url

package main

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

// gin的helloWorld

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // 路由组1 ,处理GET请求
   v1 := r.Group("/v1")
   // {} 是书写规范
   {
      v1.GET("/login", login)
      v1.GET("submit", submit)
   }
   v2 := r.Group("/v2")
   {
      v2.POST("/login", login)
      v2.POST("/submit", submit)
   }
   r.Run(":8000")
}

func login(c *gin.Context) {
   name := c.DefaultQuery("name", "jack")
   c.String(200, fmt.Sprintf("hello %s\n", name))
}

func submit(c *gin.Context) {
   name := c.DefaultQuery("name", "lily")
   c.String(200, fmt.Sprintf("hello %s\n", name))
}

//----------------解析-------------------//
这是一个使用 Gin 框架创建的 Web 应用,包含了两个路由组,分别处理 GET 和 POST 请求。

路由组通过 r.Group 方法创建,并可以添加相同路径的不同 HTTP 方法的路由。比如,这里的 /login 路由在 v1 路由组中是 GET 请求,在 v2 路由组中是 POST 请求。

在 login 和 submit 处理函数中,使用 c.DefaultQuery 获取 URL 中的查询参数,如果不存在,则使用默认值。然后,使用 c.String 方法将字符串返回给客户端。

最后,通过 r.Run 方法运行应用,默认监听在 8000 端口。

9、404页面处理

package main

import (
    "fmt"
    "net/http"

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

func main() {
    r := gin.Default()
    r.GET("/user", func(c *gin.Context) {
        //指定默认值
        //http://localhost:8080/user 才会打印出来默认的值
        name := c.DefaultQuery("name", "Gopher陈")
        c.String(http.StatusOK, fmt.Sprintf("hello %s", name))2020-08-05 09:22:11 星期三
    })
    r.NoRoute(func(c *gin.Context) {
        c.String(http.StatusNotFound, "404 not found333333")
    })
    r.Run()
}

//----------------解析-------------------//
这段代码创建了一个名为 r 的 Gin 路由器,监听 /user 路径的 GET 请求,并将查询参数 name 的值打印出来。如果请求中没有提供 name 参数,则使用默认值 "Gopher陈"。此外,它还定义了一个 NoRoute 处理函数,用于处理所有未被定义的路由。如果请求的路径不匹配任何已定义的路由,则返回 404 not found333333。

当访问 http://localhost:8080/user 时,如果没有提供 name 参数,则返回 "hello Gopher陈"。如果提供了 name 参数,则返回 "hello " + name 的值。当访问除 /user 外的任何其他路径时,将返回 404 not found333333。

10、路由原理

Gin中的路由原理是基于HTTP请求方法和请求路径的匹配,当请求到来时,Gin会遍历已注册的路由,找到匹配的路由并执行相应的处理函数。

具体来说,当我们使用Gin注册路由时,可以通过gin.EngineGETPOSTPUTDELETE等方法来注册对应请求方法的路由,如:

r := gin.Default()

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

这里注册了一个GET请求方法的路由,当请求路径为/hello时,会执行传入的匿名函数,返回"Hello World"字符串。

在路由的注册过程中,我们还可以使用参数来匹配请求路径,例如:

r.GET("/hello/:name", func(c *gin.Context) {
    name := c.Param("name")
    c.String(http.StatusOK, "Hello %s", name)
})

这里注册了一个GET请求方法的路由,使用:name参数匹配请求路径,当请求路径为/hello/xxx时,会执行传入的匿名函数,返回"Hello xxx"字符串。

总之,Gin的路由原理是根据请求方法和请求路径的匹配来执行相应的处理函数,使我们可以方便地实现RESTful API服务。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Gin框架路由解析是通过路由引擎来实现的。Gin使用httprouter作为默认的路由引擎,该引擎基于Radix树实现快速的路由匹配。 当你在Gin应用程序定义路由时,通过调用`gin.Engine`的`GET`、`POST`、`PUT`、`DELETE`等方法来注册不同的HTTP方法的路由处理函数。例如: ```go router := gin.Default() router.GET("/users/:id", getUserHandler) ``` 在路由路径,可以使用冒号(:)来定义参数,例如`:id`表示一个名为"id"的参数。当请求到达时,Gin会将请求的URL与注册的路由进行匹配。 Gin路由引擎解析过程如下: 1. Gin首先将注册的路由的每个路由路径解析成一棵树,并构建相应的路由节点。 2. 当有新的请求到达时,Gin会从根节点开始遍历路由树。 3. 对于每个节点,Gin会检查节点的路径是否与当前请求的URL路径匹配。如果匹配成功,则继续遍历该节点的子节点。 4. 如果节点是参数节点(包含冒号(:)),则将该参数值提取出来,并将其作为上下文的一部分存储起来。 5. 当遍历到叶子节点时,找到了与请求URL完全匹配的路由路径。 6. Gin将找到的路由路径对应的处理函数返回给HTTP服务器,进行后续的处理。 需要注意的是,Gin路由引擎是基于树的数据结构,因此它具有较高的匹配效率和性能。 希望这个回答对你有帮助!如果你还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder陈、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值