6、Go Gin 路由详解(之二)

一、路由详解

路由定义:

  • 路由的核心是定义 HTTP 请求的路径,即 URI(Uniform Resource Identifier)。路径可以是静态的(如 /users) 或动态的(如 /users/:id),动态路径参数以冒号 : 开头,如 :id 表示一个待捕获的变量。

HTTP 方法:

  • Gin 支持常见的 HTTP 方法,如 GETPOSTPUTDELETEPATCH 等。这些方法与路径组合,定义了特定操作与资源之间的映射关系,遵循 RESTful 设计原则。例如:
    • GET /users: 查询用户列表。
    • GET /users/:id: 获取指定 ID 的用户详情。
    • POST /users: 创建新用户。
    • PUT /users/:id: 更新指定 ID 的用户信息。
    • DELETE /users/:id: 删除指定 ID 的用户。

路由注册:

  • 使用 Gin 实例(通常称为 r)的相应 HTTP 方法(如 GETPOST 等)方法注册路由。每个方法接收两个参数:路径(字符串)和处理函数(闭包或指向函数的指针)。例如:
    r.GET("/users", getUsers)
    r.POST("/users", createUser)

路由参数与提取:

  • 动态参数:在路径中定义的动态参数(如 :id)会在请求到达时被自动捕获,并可通过 c.Param() 方法在处理函数中访问。例如:

    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        // 使用 id 查询数据库并返回用户信息
    })
  • 正则约束:可以为动态参数添加正则表达式约束,确保传入的值符合预期格式。例如,限制 id 只接受数字:

    r.GET("/users/:id(\\d+)", getUserByID)

 路由分组:

  • 为了组织和复用中间件、共享路径前缀,Gin 提供了路由分组功能。使用 Group() 方法创建分组,并可为分组应用中间件。子路由注册在分组内部,共享其前缀和中间件设置。例如:
    admin := r.Group("/admin", authMiddleware)
    {
        admin.GET("/users", listAdminUsers)
        admin.POST("/users", createAdminUser)
    }
    上述代码创建了一个名为 admin 的分组,挂载在 /admin 路径下,所有请求都需要通过 authMiddleware 中间件。

请求域获取(GET、POST)

1、Get 请求传值

// GET http://127.0.0.1:8080/user?uid=20&page=1
 // 返回uid=20 page=1
router.GET("/user", func(c *gin.Context) {
    uid := c.Query("uid")
    page := c.DefaultQuery("page", "0")//不传page参数默认返回0
    c.String(200, "uid=%v page=%v", uid, page)
})

2、Get动态路由传值

// 域名/user/20
r.GET("/user/:uid", func(c *gin.Context) {
    uid := c.Param("uid")
    c.String(200, "userID=%s", uid)
})

3、Post 请求传值 获取 form 表单数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/doAddUser" method="post">
    用户名:<input type="text" name="username" />
    密码: <input type="password" name="password" />
    <input type="submit" value="提交">
</form>
</body>
</html>
package main

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

func main() {
	// 加载引擎
	r := gin.Default()

	r.LoadHTMLGlob("templates/*")

	// 通过 c.PostForm 接收表单传过来的数据
	// http://127.0.0.1:8080/addUser
	r.GET("/addUser", func(c *gin.Context) {
		c.HTML(200, "index.html", gin.H{})
	})
	r.POST("/doAddUser", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")
		age := c.DefaultPostForm("age", "20")
		c.JSON(200, gin.H{
			"usernmae": username,
			"password": password,
			"age":      age,
		})
	})
	r.Run(":8080")
}

3、获取 GET POST 传递的数据绑定到结构体

package main

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

// 注意首字母大写
type Userinfo struct {
	Username string `form:"username" json:"user"`
	Password string `form:"password" json:"password"`
}

func main() {
	// 加载引擎
	r := gin.Default()

	//get请求:http://127.0.0.1:8080/getBind?username=sph&password=123456
	//返回{"user":"sph","password":"123456"}
	r.GET("/getBind", func(c *gin.Context) {
		var userinfo Userinfo
		//使用c.ShouldBind()绑定
		if err := c.ShouldBind(&userinfo); err == nil {
			c.JSON(http.StatusOK, userinfo)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})

	//post请求:http://127.0.0.1:8080/postBind?username=sph&password=123456
	//返回{"user":"sph","password":"123456"}
	r.POST("/postBind", func(c *gin.Context) {
		var userinfo Userinfo
		//使用c.ShouldBind()绑定
		if err := c.ShouldBind(&userinfo); err == nil {
			c.JSON(http.StatusOK, userinfo)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})
	r.Run(":8080")
}

4、获取 Post Xml 数据

<?xml version="1.0" encoding="UTF-8"?>
<article>
<content type="string">我是sph</content>
<title type="string">sph</title>
</article>
package main

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

type Article struct {
	Title   string `xml:"title"`
	Content string `xml:"content"`
}

func main() {
	// 加载引擎
	r := gin.Default()

	//post请求:http://127.0.0.1:8080/xml
	r.POST("/xml", func(c *gin.Context) {
		b, _ := c.GetRawData() // 从 c.Request.Body 读取请求数据
		article := &Article{}
		//反序列化
		if err := xml.Unmarshal(b, &article); err == nil {
			c.JSON(http.StatusOK, article)
		} else {
			c.JSON(http.StatusBadRequest, err.Error())
		}
	})

	r.Run(":8080")
}

完整代码

default.html
<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "default/user.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>news</title>
</head>
<body>
{{ template "public/page_header.html" .}}
<form action="/doAddUser" method="post">
    用户名:<input type="text" name="username"/> <br/> <br/>
    密码:<input type="text" name="password"/><br/> <br/>
    <input type="submit" value="提交">
</form>
<h2>{{UnixToTime .date}}</h2>
<h2>{{Println .hobby .motion}}</h2>
{{ template "public/page_footer.html" .}}
</body>
</html>
{{ end }}
page_footer.html 
<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "public/page_footer.html" }}
<h4>
    公共的底部
</h4>
{{end}}
page_header.html  
<!-- 相当于给模板定义一个名字, define end 必须成对出现 -->
{{ define "public/page_header.html" }}
<h1>
  公共的 --- {{.title}}
</h1>
{{end}}
go代码
package main

import (
	"encoding/xml"
	"github.com/gin-gonic/gin"
	"html/template"
	"net/http"
	"time"
)

// 时间戳转换成日期函数
func UnixToTime(timestamp int) string {
	t := time.Unix(int64(timestamp), 0)
	return t.Format("2006-01-02 15:04:05")
}

func Println(str1 string, str2 string) string {
	return str1 + "跟" + str2
}

type UserInfo struct {
	Username string `json:"username" form:"username"`
	Password string `json:"password" form:"password"`
}

type Article struct {
	Title   string `json:"title" xml:"title"`
	Content string `json:"content" xml:"content"`
}

func main() {
	//初始化路由
	r := gin.Default()
	//自定义模板函数,必须在r.LoadHTMLGlob前面
	r.SetFuncMap(template.FuncMap{
		"UnixToTime": UnixToTime, //注册模板函数
		"Println":    Println,
	})
	//加载templates中所有模板文件, 使用不同目录下名称相同的模板,注意:一定要放在配置路由之前才得行
	r.LoadHTMLGlob("templates/**/*")
	//配置静态web目录 第一个参数表示路由,第二个参数表示映射的目录
	r.Static("/static", "./static")
	//配置路由
	//GET传值http://127.0.0.1:8080/?username=sph&age=100
	r.GET("/", func(c *gin.Context) {
		username := c.Query("username")
		age := c.Query("age")
		page := c.DefaultQuery("page", "1")
		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"age":      age,
			"page":     page,
		})
	})
	//GET传值 获取文章id
	//http://127.0.0.1:8080/article?id=10
	r.GET("/article", func(c *gin.Context) {
		id := c.DefaultQuery("id", "1")
		c.JSON(http.StatusOK, gin.H{
			"id":     id,
			"conent": "文章详情",
		})
	})

	//post演示
	//http://127.0.0.1:8080/user
	r.GET("/user", func(c *gin.Context) {
		//渲染模板
		c.HTML(http.StatusOK, "default/user.html", gin.H{
			"title":  "首页",
			"date":   1714115969,
			"hobby":  "吃饭",
			"motion": "打篮球",
		})
	})
	//获取表单post过来的数据
	r.POST("/doAddUser", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")
		age := c.DefaultPostForm("age", "20")
		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"password": password,
			"age":      age,
		})
	})
	//获取GET POST传递的数据绑定到结构体
	//r.POST("/getUser", func(c *gin.Context) {
	r.GET("/getUser", func(c *gin.Context) {
		user := &UserInfo{}
		//绑定到对应的结构体
		if err := c.ShouldBind(&user); err == nil {
			c.JSON(http.StatusOK, user)
		} else {
			c.JSON(http.StatusOK, gin.H{
				"err": err.Error(),
			})
		}
	})
	//获取 POST xml数据绑定到结构体
	r.POST("/xml", func(c *gin.Context) {
		article := &Article{}
		//获取c.Request.Body读取请求数据, 返回的是一个xml切片
		xmlSliceData, _ := c.GetRawData()
		//解析xml切片
		if err := xml.Unmarshal(xmlSliceData, &article); err == nil {
			c.JSON(http.StatusOK, article)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"err": err.Error(),
			})
		}
	})

	//动态路由传值
	//http://127.0.0.1:8080/list/123
	r.GET("/list/:id", func(c *gin.Context) {
		id := c.Param("id")
		c.JSON(http.StatusOK, gin.H{
			"id": id,
		})
	})
	r.Run() // 启动一个web服务
}

路由组

1、简单路由组

package main

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

func main() {
	//初始化引擎
	r := gin.Default()
	//简单的路由分组
	defaultRouters := r.Group("/")
	{
		defaultRouters.GET("/", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "首页",
				"content": "吃饭打酱油",
			})
		})
		defaultRouters.GET("/news", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "新闻",
				"content": "吃饭打酱油",
			})
		})
	}

	apiRouters := r.Group("/api")
	{
		apiRouters.GET("/", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "API首页",
				"content": "吃饭打酱油",
			})
		})
		apiRouters.GET("/userList", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "userList",
				"content": "这是用户列表",
			})
		})
	}
	r.Run() // 启动一个web服务
}

2、项目大的时候一般需要路由分组注册

目录结构

routers包
package routers

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

// AdminRouters 设置admin后台路由
func AdminRouters(r *gin.Engine) {
	adminRouters := r.Group("/admin")
	{
		adminRouters.GET("/", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "后台首页",
				"content": "吃饭打酱油",
			})
		})
		adminRouters.GET("/admin", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "后台管理页面",
				"content": "吃饭打酱油",
			})
		})
	}
}
package routers

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

// LoginRouters 登录注册页面路由
func LoginRouters(r *gin.Engine) {
	loginRouters := r.Group("/")
	{
		loginRouters.GET("/", func(c *gin.Context) {
			c.HTML(http.StatusOK, "login/login.html", gin.H{
				"msg": "登录页面",
			})
		})
		loginRouters.GET("/register", func(c *gin.Context) {
			c.HTML(http.StatusOK, "register/register.html", gin.H{
				"msg": "注册页面",
			})
		})
	}
}
package routers

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

// UserRouters 设置用户api路由
func UserRouters(r *gin.Engine) {
	apiRouters := r.Group("/userApi")
	{
		apiRouters.GET("/", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "用户",
				"content": "用户",
			})
		})
		apiRouters.GET("/userList", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"title":   "用户列表",
				"content": "用户列表内容",
			})
		})
	}
}
 main.go
package main

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

func main() {
	r := gin.Default()
	//注册路由
	routers.LoginRouters(r)
	routers.AdminRouters(r)
	routers.AdminRouters(r)
}

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值