文章目录
- 模版使用流程
- 参数传递
- 路由分组
- 数据解析和绑定
- gin中间件
模版使用流程
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 1.创建路由
r := gin.Default()
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "hello World!")
})
// 3.监听端口,默认在8080
// Run("里面不指定端口号默认为8080")
r.Run(":8000")
}
运行,访问:http://localhost:8000/
后面为了减少累赘代码,只展示路由规则部分代码编写,创建路由,以及端口监听部分代码保存相同。
参数传递
- API传参:可以通过Context的
Param
方法来获取API参数。
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello "+name+"!")
})
r.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.String(http.StatusOK, "Hello "+name+"!"+"Action "+action)
})
*action
表示一个通配路径参数,可以匹配 URL 剩余部分的路径,并且会包含匹配部分的斜杠 /
。
- URL传参:URL参数可以通过
DefaultQuery()
或Query()
方法获取,DefaultQuery()
若参数不存在,返回默认值,Query()
若不存在,返回空串。
DefaultQuery默认值只支持string类型:
func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok {
return value
}
return defaultValue
}
r.GET("/user", func(c *gin.Context) {
name := c.DefaultQuery("name", "Test")
ageStr := c.DefaultQuery("age", "18")
age, err := strconv.Atoi(ageStr)
if err != nil {
c.String(http.StatusBadRequest, "Invalid age")
return
}
c.String(http.StatusOK, fmt.Sprintf("%s is %d years old", name, age))
})
- 表单传值
<form action="http://localhost:8000/form" method="post" action="application/x-www-form-urlencoded">
用户名:<input type="text" name="username" placeholder="请输入你的用户名"> <br>
密 码:<input type="password" name="userpassword" placeholder="请输入你的密码"> <br>
<input type="submit" value="提交">
</form>
r.POST("/form", func(c *gin.Context) {
types := c.DefaultPostForm("type", "post")
username := c.PostForm("username")
password := c.PostForm("userpassword")
c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
})
- 文件上传
单个文件:
<form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
上传文件:<input type="file" name="file" >
<input type="submit" value="提交">
</form>
// 限制上传文件大小 (10 MiB)
r.MaxMultipartMemory = 10 << 20 // 10 MiB
// 文件上传路由
r.POST("/upload", func(c *gin.Context) {
// 获取文件
file, err := c.FormFile("file")
if err != nil {
c.String(http.StatusBadRequest, "File upload error: %s", err.Error())
return
}
// 检查文件类型 (仅允许 image/png)
mimeType := file.Header.Get("Content-Type")
if mimeType != "image/png" {
c.String(http.StatusBadRequest, "Invalid file type: %s. Only PNG images are allowed.", mimeType)
return
}
// 保存文件到目标路径
dst := filepath.Join("./uploads", file.Filename)
if err := c.SaveUploadedFile(file, dst); err != nil {
c.String(http.StatusInternalServerError, "Could not save file: %s", err.Error())
return
}
c.String(http.StatusOK, "File uploaded successfully: %s", file.Filename)
})
多个文件:
// 限制上传文件大小 (10 MiB)
r.MaxMultipartMemory = 10 << 20 // 10 MiB
// 多文件上传路由
r.POST("/upload/multiple", func(c *gin.Context) {
// 获取表单文件 (key 为 "files")
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest, "Form error: %s", err.Error())
return
}
files := form.File["files"] // 获取多个文件
for _, file := range files {
// 检查文件类型 (仅允许 image/png)
mimeType := file.Header.Get("Content-Type")
if mimeType != "image/png" {
c.String(http.StatusBadRequest, "Invalid file type: %s for file: %s. Only PNG images are allowed.", mimeType, file.Filename)
return
}
// 保存文件到目标路径
dst := filepath.Join("./uploads", file.Filename)
if err := c.SaveUploadedFile(file, dst); err != nil {
c.String(http.StatusInternalServerError, "Could not save file: %s. Error: %s", file.Filename, err.Error())
return
}
fmt.Printf("Uploaded file: %s\n", file.Filename)
}
c.String(http.StatusOK, "All files uploaded successfully")
})
路由分组
在 Gin 框架中,路由分组(Route Group)是用于组织和管理路由的功能。通过路由分组,可以为一组路由添加公共的前缀、中间件或配置,从而提高代码的可读性和可维护性。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// 1.创建路由
r := gin.Default()
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
// 用户路由组
userGroup := r.Group("/user")
{
userGroup.GET("/login", login)
userGroup.GET("/settings", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "User Settings"})
})
}
// 管理员路由组
adminGroup := r.Group("/admin")
{
adminGroup.GET("/dashboard", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Admin Dashboard"})
})
adminGroup.GET("/reports", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Admin Reports"})
})
}
// 404
r.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "404 not found !!!!")
})
// 3.监听端口,默认在8080
// Run("里面不指定端口号默认为8080")
r.Run(":8000")
}
func login(c *gin.Context) {
name := c.DefaultQuery("name", "jack")
c.JSON(200, gin.H{"name": name})
}
数据解析和绑定
- JSON数据解析和绑定的基本步骤
(1) 定义结构体
定义一个与 JSON 数据格式匹配的 Go 结构体。
(2) 使用 c.BindJSON
或 c.ShouldBindJSON
c.BindJSON
会在绑定失败时返回错误并终止请求。c.ShouldBindJSON
返回错误但不会中断后续处理,适合灵活的错误处理场景。
(3) 使用结构体处理数据
通过绑定的结构体直接访问解析后的字段。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type User struct {
Name string `json:"name" binding:"required"` // 必填字段
Age int `json:"age" binding:"required"` // 必填字段
Email string `json:"email"` // 可选字段
}
func main() {
r := gin.Default()
r.POST("/user", func(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "User data received",
"user": user,
})
})
r.Run(":8080")
}
Gin 提供了 binding
标签,用于对字段进行验证。支持多种验证规则。
type User struct {
Name string `json:"name" binding:"required,min=3"` // 必填,最小长度 3
Age int `json:"age" binding:"required,gte=1"` // 必填,必须大于等于 1
Email string `json:"email" binding:"required,email"` // 必填,必须是合法邮箱
}
验证规则 | 含义 |
---|---|
required | 必须提供该字段 |
min | 字符串或切片的最小长度 |
max | 字符串或切片的最大长度 |
gte | 数字或时间的值必须大于等于指定值 |
lte | 数字或时间的值必须小于等于指定值 |
email | 必须是有效的邮箱地址 |
- 表单数据:
为字段添加多种绑定方式的标签,例如json
和form
标签。
Gin 会根据 Content-Type
自动选择适合的绑定方式:
- 如果是
application/json
,则绑定 JSON 数据。 - 如果是
application/x-www-form-urlencoded
或multipart/form-data
,则绑定表单数据。
type User struct {
Name string `json:"name" form:"name" binding:"required"` // 必填字段
Age int `json:"age" form:"age" binding:"required,gte=0"`
Email string `json:"email" form:"email"`
}
- URI数据
通过 c.Param 获取路径参数的值
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id") // 提取路径参数
c.JSON(http.StatusOK, gin.H{
"user_id": id,
})
})
r.Run(":8080")
}
Gin 提供了 ShouldBindUri
方法,可以将路径参数直接绑定到结构体中,方便处理和验证。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type UserRequest struct {
ID string `uri:"id" binding:"required"` // 使用 `uri` 标签定义路径参数
}
func main() {
r := gin.Default()
r.GET("/user/:id", func(c *gin.Context) {
var req UserRequest
// 将路径参数绑定到结构体
if err := c.ShouldBindUri(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"user_id": req.ID,
})
})
r.Run(":8080")
}
- 路径参数可以和查询参数或表单参数结合使用。Gin 支持同时解析这些参数。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type UserRequest struct {
ID string `uri:"id" binding:"required"` // 路径参数
Name string `form:"name"` // 查询参数或表单参数
}
func main() {
r := gin.Default()
r.GET("/user/:id", func(c *gin.Context) {
var req UserRequest
// 绑定路径参数
if err := c.ShouldBindUri(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 绑定查询参数
if err := c.ShouldBindQuery(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"user_id": req.ID,
"name": req.Name,
})
})
r.Run(":8080")
}
GET /user/123?name=Alice
gin中间件
在 Gin 中,中间件是一个可以插入到请求处理流程中的函数,用于实现请求前后的通用逻辑。例如,认证、日志记录、跨域处理等都可以通过中间件实现。
- 中间件定义
func(c *gin.Context) {
// 执行逻辑
c.Next() // 调用后续处理函数
// 执行逻辑
}
c.Next()
:调用下一个中间件或处理程序。
c.Abort()
:停止执行后续的中间件和处理程序。
- 全局中间件
全局中间件会应用到所有路由,使用 r.Use()
注册。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
// 请求前逻辑
fmt.Println("Request URL:", c.Request.URL.Path)
c.Next() // 执行后续处理
// 请求后逻辑
fmt.Println("Response Status:", c.Writer.Status())
}
}
func main() {
r := gin.Default()
// 注册全局中间件
r.Use(Logger())
r.GET("/ping", func(c *gin.Context) {
fmt.Println("=======11111111")
c.String(200, "pong")
})
r.Run(":8080")
}
控制台输出:
Request URL: /ping
=======11111111
Response Status: 200
流程:中间件逻辑=》遇到next方法后,执行路由func
=》中间件后面的逻辑。
- 局部中间件
局部中间件只应用于特定路由或路由组。
package main
import (
"github.com/gin-gonic/gin"
)
func AuthRequired() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token != "valid-token" {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return
}
c.Next()
}
}
func main() {
r := gin.Default()
// 局部中间件
r.GET("/secure", AuthRequired(), func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Authorized"})
})
r.Run(":8080")
}