Gin是一个用于构建Web应用程序的轻量级框架,基于Go语言开发。它提供了简单、高效和灵活的API,使得开发Web应用程序和API变得容易。Gin的设计理念是尽可能快速地处理HTTP请求,并提供易于理解和使用的接口。
快速入门
- 通过
gin.Default()创建了一个带有默认中间件的Gin引擎 - 通过
r.GET定义路由以及它的处理函数 - 通过
r.Run()启动Gin服务器
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
Gin路由
在Gin中,路由用于将HTTP请求映射到相应的处理函数。Gin提供了多种方式来定义和设置路由,包括基本路由、路由组、参数路由等。以下是Gin中路由的基本使用方法
基本路由
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 定义基本路由,处理GET请求到根路径"/"
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello, Gin!")
})
r.Run(":8080")
}
路由组
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 定义路由组,处理GET请求到"/api"路径
api := r.Group("/api")
{
api.GET("/users", func(c *gin.Context) {
c.String(200, "List of users")
})
api.POST("/users", func(c *gin.Context) {
c.String(200, "Create a new user")
})
}
r.Run(":8080")
}
参数路由
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 定义带参数的路由,处理GET请求到路径"/user/:id"
r.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "User ID: "+id)
})
r.Run(":8080")
}
路由匹配顺序
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 第一个路由,精确匹配
r.GET("/users", func(c *gin.Context) {
c.String(200, "List of users")
})
// 第二个路由,通配符匹配
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "User ID: "+id)
})
r.Run(":8080")
}
响应数据
package main
import (
"github.com/gin-gonic/gin"
)
// ResponseData 表示响应数据的结构体
type ResponseData struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Msg string `json:"msg"`
}
func main() {
r := gin.Default()
// 定义基本路由,处理GET请求到根路径"/"
r.GET("/", func(c *gin.Context) {
// 创建一个ResponseData结构体实例,用于构造响应数据
respData := ResponseData{
Code: 200,
Data: gin.H{"message": "Hello, Gin!"},
Msg: "Success",
}
// 使用JSON方法将ResponseData作为JSON响应数据返回给客户端
c.JSON(200, respData)
})
r.Run(":8080")
}
在Gin中,
gin.H是一个简化的map[string]interface{}类型,用于在处理HTTP请求时构建和处理JSON响应的键值对。使用gin.H构造JSON十分方便。
Gin中间件
在Gin中,中间件是一种在处理请求和发送响应之间执行的功能。中间件可以用于访问请求和响应参数,执行某些逻辑,或者在请求传递给路由处理函数之前进行预处理。
中间件是通过使用gin.HandlerFunc类型来实现的。下面是一个简单的中间件示例,它在请求到达路由处理函数之前输出日志信息:
基本使用
通过r.Use()配置中间件
package main
import (
"github.com/gin-gonic/gin"
"time"
)
// LoggerMiddleware 是自定义的日志中间件
func LoggerMiddleware(c *gin.Context) {
// 在处理请求之前记录请求的起始时间
start := time.Now()
// 处理请求,继续向下执行
c.Next()
// 在请求处理完成后记录请求处理时间
elapsed := time.Since(start)
// 输出日志信息
c.Writer.WriteString("Request took: " + elapsed.String())
}
func main() {
r := gin.Default()
// 使用LoggerMiddleware中间件
r.Use(LoggerMiddleware)
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello, Gin!")
})
r.Run(":8080")
}
中间件执行顺序
中间件的执行顺序是按照添加它们的顺序进行的,先添加的中间件先执行,后添加的中间件后执行。在每个中间件函数中,通过c.Next()调用链实现中间件的连续执行。
示例:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 第一个中间件
r.Use(func(c *gin.Context) {
c.Writer.WriteString("First Middleware - Before\n")
c.Next()
c.Writer.WriteString("First Middleware - After\n")
})
// 第二个中间件
r.Use(func(c *gin.Context) {
c.Writer.WriteString("Second Middleware - Before\n")
c.Next()
c.Writer.WriteString("Second Middleware - After\n")
})
// 路由处理函数
r.GET("/", func(c *gin.Context) {
c.Writer.WriteString("Main Handler\n")
})
r.Run(":8080")
}
在这个程序启动后,当访问根路径/时,输出结果如下:
First Middleware - Before
Second Middleware - Before
Main Handler
Second Middleware - After
First Middleware - After
Gin中,中间件(Middleware)的角色更接近于Spring框架中的拦截器(Interceptor)
- 执行位置:中间件和拦截器都可以在请求处理流程的开始和结束时执行,允许在请求被处理之前和之后执行一些逻辑。
- 功能:它们都可以用于实现一些公共的预处理逻辑,如日志记录、权限验证、请求参数处理等,使得这些逻辑可以被多个请求共享和复用。
- 顺序:它们的执行顺序都可以通过配置来决定,可以按照添加的顺序执行
Gin控制器
在Gin中,没有严格的"控制器"概念,但我们可以将Gin中的路由处理函数类比为MVC模式中的控制器。路由处理函数负责接收HTTP请求,处理请求参数、业务逻辑,并生成相应的HTTP响应。
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 定义路由,处理GET请求到根路径"/"
r.GET("/", IndexHandler)
// 定义路由,处理GET请求到路径"/hello"
r.GET("/hello", HelloHandler)
r.Run(":8080")
}
// IndexHandler 处理根路径"/"的路由
func IndexHandler(c *gin.Context) {
c.String(200, "Hello, Gin!")
}
// HelloHandler 处理路径"/hello"的路由
func HelloHandler(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello, Gin!",
})
}
在这个示例中,我们定义了两个路由处理函数 IndexHandler 和 HelloHandler。IndexHandler 处理根路径"/"的GET请求,并直接返回"Hello, Gin!"字符串作为响应。HelloHandler 处理"/hello"路径的GET请求,返回一个包含"message"字段的JSON响应。
Gin文件上传
单文件
func main() {
router := gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 单文件
file, _ := c.FormFile("file")
log.Println(file.Filename)
dst := "./" + file.Filename
// 上传文件至指定的完整文件路径
c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
多文件
func main() {
router := gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["upload[]"]
for _, file := range files {
log.Println(file.Filename)
// 上传文件至指定目录
c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
router.Run(":8080")
}
Gin Model
在Gin中,通常将 Model 定义为普通的 Go 结构体(struct),用于表示数据库表的行、业务实体或其他数据结构。Model 包含了数据的字段,以及可能的一些方法用于操作或处理数据。Gin Model类似于Java中的实体类。
以下为一个示例:
package main
import "time"
// User 是一个简单的用户数据模型
type User struct {
ID uint `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Age int `json:"age"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Model绑定
在 Gin 框架中,Model 绑定是一种方便的功能,用于将请求中的数据绑定到指定的结构体(Model)。Gin 提供了几个函数来实现 Model 绑定,包括 c.Bind、c.ShouldBind 和 c.ShouldBindWith。它们的主要区别在于处理绑定失败的方式以及对 Content-Type 的处理。
-
c.Bind:c.Bind是最简单的 Model 绑定方法,它会根据请求的 Content-Type 来自动选择合适的绑定器(Binder),并将请求中的数据绑定到指定的结构体。如果绑定失败,将返回错误,并且应该由开发者进行错误处理。// 使用 c.Bind 绑定请求数据到指定结构体 if err := c.Bind(&user); err != nil { // 处理绑定失败的情况 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } -
c.ShouldBind:c.ShouldBind类似于c.Bind,它也会根据请求的 Content-Type 来选择合适的绑定器。但是,不同之处在于c.ShouldBind会在绑定失败时返回错误,但同时也会继续处理请求。这意味着即使绑定失败,您仍然可以继续使用绑定之前的数据。// 使用 c.ShouldBind 绑定请求数据到指定结构体 if err := c.ShouldBind(&user); err != nil { // 可以继续处理请求,即使绑定失败 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } // 继续处理请求... -
c.ShouldBindWith:c.ShouldBindWith允许您使用自定义的绑定器来处理请求数据,而不是自动选择合适的绑定器。您可以在c.ShouldBindWith中指定一个绑定器(Binder),用于根据请求的 Content-Type 进行绑定。// 使用 c.ShouldBindWith 绑定请求数据到指定结构体,指定 JSON 绑定器 if err := c.ShouldBindWith(&user, binding.JSON); err != nil { // 处理绑定失败的情况 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return }
绑定器(Binder)是一个实现了 binding.Binder 接口的对象,Gin 默认提供了多个绑定器,包括 binding.JSON、binding.XML、binding.Form 等,它们用于处理不同的 Content-Type。在绝大多数情况下,使用 c.Bind 或 c.ShouldBind 即可满足大部分需求,Gin 会自动选择合适的绑定器。
Cookie & Session
入门
Cookie:
Cookie 是一种在客户端(通常是浏览器)存储少量数据的机制。它可以由服务器发送给客户端,并随着每个请求一起被发送回服务器。Cookie 通常用于:
- 会话管理:用于跟踪用户的会话状态,使得服务器可以识别每个不同的用户。
- 记住用户:可以将一些用户特定的信息保存在 Cookie 中,例如用户偏好设置、购物车内容等。
Cookie 存储在客户端的浏览器中,并可以设置过期时间,可以是会话级的(在浏览器关闭后失效)或持久的(设置了过期时间)。
在 Gin 中,您可以使用 c.SetCookie() 方法设置和发送 Cookie,以及使用 c.Cookie() 方法读取客户端发送回的 Cookie。
Session:
Session 是一种在服务器端存储用户状态和数据的机制。每个用户会话在服务器上有一个对应的数据存储,而客户端只需要保存一个唯一的 Session ID,通常通过 Cookie 发送给客户端。
Session 通常用于:
- 认证和用户登录:将用户的登录状态存储在 Session 中,允许用户在同一会话中保持登录状态。
- 用户状态跟踪:将用户在不同页面或请求之间的状态和数据存储在 Session 中,实现持久化跟踪。
在 Gin 中,可以使用第三方中间件(例如 github.com/gin-contrib/sessions)来实现 Session 管理。这些中间件可以处理 Session ID 的生成和管理,以及将 Session 数据存储在内存、数据库或其他存储介质中。
以下是一个简单的 Gin 示例代码,展示了如何使用 Cookie 和 Session:
package main
import (
"github.com/gin-gonic/gin"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
)
func main() {
r := gin.Default()
// 设置使用 Cookie 存储 Session 数据
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
// 设置路由,处理设置 Cookie 的请求
r.GET("/setcookie", func(c *gin.Context) {
c.SetCookie("username", "john", 3600, "/", "localhost", false, true)
c.String(200, "Cookie set successfully!")
})
// 设置路由,处理读取 Cookie 的请求
r.GET("/getcookie", func(c *gin.Context) {
username, err := c.Cookie("username")
if err != nil {
c.String(400, "Cookie not found")
return
}
c.String(200, "Username: "+username)
})
// 设置路由,处理设置 Session 的请求
r.GET("/setsession", func(c *gin.Context) {
session := sessions.Default(c)
session.Set("user_id", 123)
session.Save()
c.String(200, "Session set successfully!")
})
// 设置路由,处理读取 Session 的请求
r.GET("/getsession", func(c *gin.Context) {
session := sessions.Default(c)
userID := session.Get("user_id")
c.String(200, "User ID: %v", userID)
})
r.Run(":8080")
}
在上述示例中,使用了第三方中间件 github.com/gin-contrib/sessions 来实现 Session 管理。通过 cookie.NewStore([]byte("secret")) 创建了一个基于 Cookie 的 Session 存储。
其中定义了四个路由来演示设置和读取 Cookie 和 Session 的过程。/setcookie 路由设置了一个名为 "username" 的 Cookie,/getcookie 路由读取了 "username" Cookie 的值。/setsession 路由设置了一个名为 "user_id" 的 Session 数据,/getsession 路由读取了 "user" 的 Session 数据。
Api详解
-
c.SetCookie():设置一个 HTTP Cookie 并将其发送给客户端。该方法的签名如下:func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)name:Cookie 名称。value:Cookie 值。maxAge:Cookie 的过期时间,以秒为单位。设置为负值表示在浏览器关闭时过期,设置为 0 表示删除 Cookie,设置为正值表示指定秒数后过期。path:Cookie 适用的路径。domain:Cookie 适用的域名。secure:是否只在 HTTPS 连接中传输 Cookie。httpOnly:是否只允许 HTTP 协议访问 Cookie,禁止 JavaScript 访问。
-
c.Cookie():从客户端请求中获取指定名称的 Cookie。该方法的签名如下:func (c *Context) Cookie(name string) (string, error)name:要获取的 Cookie 名称。
如果指定名称的 Cookie 存在于客户端请求中,该方法返回 Cookie 的值;否则,返回错误信息。
-
cookie.NewStore():创建一个新的 Cookie 存储。该方法的签名如下:func NewStore(keyPairs ...[]byte) StorekeyPairs:用于加密 Session 数据的密钥对,可以指定多个,用于对 Session 数据进行加密。
-
sessions.Sessions():设置当前 Gin 引擎使用的 Session 中间件。该方法的签名如下:func Sessions(name string, store Store) gin.HandlerFunc-
name:Session 名称,用于在 Cookie 中存储 Session ID。 -
store:Session 存储,指定了 Session 数据的存储方式。
3,4结合可以理解为手动存储一个SessionId的Cookie,用于以后读取Session
store可以设置的更为复杂,例如
const secret = "mycomplexrandomsecretkey_@#(*$" store := cookie.NewStore([]byte(secret)) -
-
sessions.Default():获取当前请求的 Session 对象。该方法的签名如下:func Default(c *gin.Context) Sessionc:当前 Gin 请求上下文。
通过该方法,可以获取当前请求的 Session 对象,用于存取和管理 Session 数据。
-
session.Set():设置 Session 数据的键值对。该方法的签名如下:func (session Session) Set(key string, value interface{})key:Session 数据的键。value:Session 数据的值。
-
session.Get():获取 Session 数据的值。该方法的签名如下:func (session Session) Get(key string) interface{}key:Session 数据的键。
通过该方法,可以获取指定键对应的 Session 数据值。