路由原理
Gin 框架作为一个轻量级的 Web 框架,其路由基本原理就是构造一个路由地址的前缀树,路由使用的是httprouter这个库。
路由参数
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")}- 对于第一个路由,输入的路由参数必须要有 username 和 password,但凡少了其中的一个,就会报错(返回 404,也就是匹配不到路径)。也就是说有“ : ”的 Path 参数是必须要使用的路由参数。
- 对于第二个路由,输入的路由参数必须要有 name 参数,可以没有 age 参数,当不输入 age 参数时将会重定向到/user/name/路由下面。也就是说有“ * ”的 Path 参数不是必须的路由参数。
测试结果如下:
- 第一组路由:
- 第二组路由:
普通路由
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 方法如下:
r.Any("/index", func(c *gin.Context) { //TODO})还可以为没有匹配处理函数的路由添加处理程序,默认情况下它返回 404 代码,下面的代码为没有匹配到路由的请求都返回 404.html 页面。
r.NoRoute(func(c *gin.Context) { c.HTML(http.StatusNotFound, "404.html", nil)})路由组
将拥有共同 URL 前缀的路由划分为一个路由组。习惯性一对{ }包裹同组的路由,这只是为了看着清晰,本质上用不用{ }包裹,其功能上没什么区别。官方文档示例代码如下:
// 简单的路由组: v1v1 := router.Group("/v1"){ v1.POST("/login", loginEndpoint) v1.POST("/submit", submitEndpoint)}// 简单的路由组: v2v2 := router.Group("/v2"){ v2.POST("/login", loginEndpoint) v2.POST("/submit", submitEndpoint)}其中路由组也是支持嵌套的,示例代码如下:
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 包中关于重定向状态码的定义
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.9HTTP 重定向
r.GET("/test1", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")})路由重定向
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"})})结果如下: