一、路由详解
路由定义:
- 路由的核心是定义 HTTP 请求的路径,即 URI(Uniform Resource Identifier)。路径可以是静态的(如
/users
) 或动态的(如/users/:id
),动态路径参数以冒号:
开头,如:id
表示一个待捕获的变量。
HTTP 方法:
- Gin 支持常见的 HTTP 方法,如
GET
、POST
、PUT
、DELETE
、PATCH
等。这些方法与路径组合,定义了特定操作与资源之间的映射关系,遵循 RESTful 设计原则。例如:GET /users
: 查询用户列表。GET /users/:id
: 获取指定 ID 的用户详情。POST /users
: 创建新用户。PUT /users/:id
: 更新指定 ID 的用户信息。DELETE /users/:id
: 删除指定 ID 的用户。
路由注册:
- 使用 Gin 实例(通常称为
r
)的相应 HTTP 方法(如GET
、POST
等)方法注册路由。每个方法接收两个参数:路径(字符串)和处理函数(闭包或指向函数的指针)。例如: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)
}