Gin 框架---路由和重定向

标签:Go语言首次发布:2023-11-13最近修改:2024-04-02

路由原理

Gin 框架作为一个轻量级的 Web 框架,其路由基本原理就是构造一个路由地址的前缀树,路由使用的是httprouter这个库。

路由参数

go
func main() {    r := gin.Default()        r.GET("/login/:username/:password", func(c *gin.Context) {        username := c.Param("username")        password := c.Param("password")        c.String(http.StatusOK, "%s,%s", username, password)    })    r.GET("/user/:name/*age", func(c *gin.Context) {        name := c.Param("name")        age := c.Param("age")        c.JSON(http.StatusOK, gin.H{            "name": name,            "age":  age,        })    })    r.Run(":8080")}
  1. 对于第一个路由,输入的路由参数必须要有 username 和 password,但凡少了其中的一个,就会报错(返回 404,也就是匹配不到路径)。也就是说有“ : ”的 Path 参数是必须要使用的路由参数。
  2. 对于第二个路由,输入的路由参数必须要有 name 参数,可以没有 age 参数,当不输入 age 参数时将会重定向到/user/name/路由下面。也就是说有“ * ”的 Path 参数不是必须的路由参数。

测试结果如下:

  1. 第一组路由:
image-20230330142301628 image-20230330142218796
  1. 第二组路由:
image-20230330142421173 image-20230330142452495

普通路由

go
r.GET("/index", func(c *gin.Context) {...})r.POST("/index", func(c *gin.Context) {...})r.PUT("/index", func(c *gin.Context) {...})r.DELETE("/index", func(c *gin.Context) {...})

此外,还有一个可以匹配所有请求方法的 Any 方法如下:

go
r.Any("/index", func(c *gin.Context) {    //TODO})

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

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

路由组

将拥有共同 URL 前缀的路由划分为一个路由组。习惯性一对{ }包裹同组的路由,这只是为了看着清晰,本质上用不用{ }包裹,其功能上没什么区别。官方文档示例代码如下:

go
// 简单的路由组: v1v1 := router.Group("/v1"){    v1.POST("/login", loginEndpoint)    v1.POST("/submit", submitEndpoint)}// 简单的路由组: v2v2 := router.Group("/v2"){    v2.POST("/login", loginEndpoint)    v2.POST("/submit", submitEndpoint)}

其中路由组也是支持嵌套的,示例代码如下:

go
shopping := r.Group("/shop"){    shopping.GET("/index", shop)    // 嵌套路由组    view := shopping.Group("view")    view.GET("/home", view)}

通常会将路由分组用在划分业务逻辑或划分 API 版本时。

重定向

状态码

3XX 状态码是关于重定向的,常见的状态码有:301,302,303,304,307 和 308。这些状态码大致可以分为三类,其中包括:

  • 永久重定向:301,308
  • 临时重定向:302,303, 307
  • 其他重定向:304

永久重定向

永久重定向:是指用户请求的资源地址已经废弃了,现在需要使用新地址来访问,并通过响应 Header 的 Location 字段将这个新的地址告知给用户。

就好比胡同口有一家我们常去的饭店,但是由于旧城改造这家店搬迁到了另一个地方。有一天我们准备去这家店吃饭,发现门口张贴告示:此店已迁移到 XXX,并附上了详细的新地址。也就是说,之后如果我还想去这家店吃饭,只能去新的地址。

永久重定向中 301 和 308 的区别:

如果浏览器发送的是 GET 请求,301 和 308 没有区别。但如果是 POST 请求,会有以下区别

  • 在 301 中,浏览器用 POST 请求服务器的/aaa 地址,但是/aaa 地址的资源已经永久废弃,服务器将请求重定向到/bbb 的地址,此时原先的 POST 请求会变成 GET 请求。
  • 在 308 中,浏览器用 POST 请求服务器的/aaa 地址,但是/aaa 地址的资源已经永久废弃,服务器将请求重定向到/bbb 的地址,此时原先的 POST 请求还是 POST 请求。

临时重定向

临时重定向:由于某些原因,导致用户请求的资源地址临时不可用,但其他某个地址是可访问的,于是就通过响应 Header 的 Location 字段将这个临时地址告知给用户。

就好比我们去一家饭店吃饭,到门口发现老板张贴告示:家里临时有事,请去附近另一个分店用餐,并附上了分店的详细地址。这样,我们就可以先临时去这家分店用餐,等之后老板回来了,我们又可以在原来这家店继续用餐。

临时重定向中 302、303 和 307 的区别:

为什么永久重定向是两个状态码,而临时重定向却有三个状态码,这是与 http 协议的发展历史有很大关系。

在 http1.0 时期:

  • 文档规定:浏览器第一次请求的方法要和重定向时候的请求方法保持一致。

  • 在浏览器实现上:若用户第一次为 POST 请求,浏览器询问用户是否向新 URL 发送请求,并强制使用 GET 发送第二个请求(与文档规定有所差异)。

在 http1.1 时期:

​ 为了弥补 http1.0 时期文档中 302 和浏览器实现上 302 的偏差,这才逐渐补充了 303 和 307 的状态码。

现在:

  • 302:不管浏览器第一次请求的方法是什么,在重定向的时候请求方法被强制为 GET 方法。
  • 303:不管浏览器第一次请求的方法是什么,在重定向的时候请求方法被强制为 GET 方法。
  • 307:浏览器第一次请求的方法和重定向时的请求方法保持一致。

net 包中关于重定向状态码的定义

go
StatusMultipleChoices   = 300 // RFC 9110, 15.4.1StatusMovedPermanently  = 301 // RFC 9110, 15.4.2StatusFound             = 302 // RFC 9110, 15.4.3StatusSeeOther          = 303 // RFC 9110, 15.4.4StatusNotModified       = 304 // RFC 9110, 15.4.5StatusUseProxy          = 305 // RFC 9110, 15.4.6_                       = 306 // RFC 9110, 15.4.7 (Unused)StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8StatusPermanentRedirect = 308 // RFC 9110, 15.4.9

HTTP 重定向

go
r.GET("/test1", func(c *gin.Context) {    c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")})

路由重定向

go
r.GET("/test2", func(c *gin.Context) {    c.Request.URL.Path = "/test3"  //在上下文中改变路由的路径    r.HandleContext(c)             //把修改后的上下文写入到路由中})r.GET("/test3", func(c *gin.Context) {    c.JSON(http.StatusOK,gin.H{"msg":"This is /test3"})})

结果如下:

image-20230330181035651