路由参数是 URL 路径中的动态部分,比如
/users/123中的123。Gin 提供了方便的方法来获取这些参数。
使用 :param 语法定义路径参数:
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{"user_id": id})
})
访问 /users/123,id 的值就是 123。
r.GET("/users/:userId/posts/:postId", func(c *gin.Context) {
userId := c.Param("userId")
postId := c.Param("postId")
c.JSON(200, gin.H{
"user_id": userId,
"post_id": postId,
})
})
访问 /users/123/posts/456,会得到 userId=123 和 postId=456。
通配符 * 可以匹配剩余的所有路径:
r.GET("/files/*filepath", func(c *gin.Context) {
filepath := c.Param("filepath")
c.JSON(200, gin.H{"path": filepath})
})
访问 /files/a/b/c.txt,filepath 的值是 /a/b/c.txt。
r.GET("/users/:id/files/*filepath", func(c *gin.Context) {
id := c.Param("id")
filepath := c.Param("filepath")
c.JSON(200, gin.H{
"user_id": id,
"filepath": filepath,
})
})
访问 /users/123/files/docs/report.pdf,会得到 id=123 和 filepath=/docs/report.pdf。
获取参数后通常需要验证:
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(400, gin.H{"error": "用户 ID 不能为空"})
return
}
c.JSON(200, gin.H{"user_id": id})
})
import "strconv"
r.GET("/users/:id", func(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.Atoi(idStr)
if err != nil {
c.JSON(400, gin.H{"error": "无效的用户 ID"})
return
}
c.JSON(200, gin.H{"user_id": id})
})
Params() 返回所有路径参数:
r.GET("/users/:userId/posts/:postId", func(c *gin.Context) {
params := c.Params()
for _, param := range params {
fmt.Printf("%s = %s\n", param.Key, param.Value)
}
c.JSON(200, params)
})
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
)
func main() {
r := gin.Default()
r.GET("/users/:id", getUser)
r.GET("/users/:userId/posts/:postId", getUserPost)
r.GET("/files/*filepath", getFile)
r.GET("/search/:category/*query", search)
r.Run(":8080")
}
func getUser(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.Atoi(idStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "用户 ID 必须是数字",
})
return
}
if id <= 0 {
c.JSON(http.StatusBadRequest, gin.H{
"error": "用户 ID 必须大于 0",
})
return
}
c.JSON(http.StatusOK, gin.H{
"id": id,
"name": fmt.Sprintf("User %d", id),
})
}
func getUserPost(c *gin.Context) {
userId := c.Param("userId")
postId := c.Param("postId")
c.JSON(http.StatusOK, gin.H{
"user_id": userId,
"post_id": postId,
})
}
func getFile(c *gin.Context) {
filepath := c.Param("filepath")
if filepath == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "文件路径不能为空",
})
return
}
c.JSON(http.StatusOK, gin.H{
"filepath": filepath,
"message": "文件内容",
})
}
func search(c *gin.Context) {
category := c.Param("category")
query := c.Param("query")
c.JSON(http.StatusOK, gin.H{
"category": category,
"query": query,
"results": []string{"result 1", "result 2"},
})
}
当有多个路由可能匹配时,Gin 会选择最具体的:
r.GET("/users/new", func(c *gin.Context) {
c.JSON(200, gin.H{"action": "create new user"})
})
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{"user_id": id})
})
访问 /users/new 会匹配第一个路由,而不是第二个。
这一章学习了:
:param 定义路径参数*param 定义通配符参数