go语言中间件理解

go语言中间件理解 // package main // import ( // net/http // time // fmt // github.com/gin-gonic/gin // ) // // // // 1. 数据结构定义 (类似 TypeScript 的 Interface) // // // // User 结构体 // // 前端视角这就好比一个 TypeScript 的 Interface 或 JSON 对象类型定义。 // // 注意字段首字母必须大写如 Name否则 Go 语言规定它不能被外部包访问类似 private。 // // json:name 是标签Tag告诉 Gin 在序列化/反序列化 JSON 时Go 的 Name 对应 JSON 的 name。 // type User struct { // ID int json:id // Name string json:name binding:required // binding:required 类似前端表单验证的 required 规则 // Email string json:email // Password string json:- // json:- 表示这个字段在返回 JSON 给前端时会被忽略类似敏感数据不脱敏 // } // // Response 统一响应结构 // // 前端视角这是你希望后端返回的标准格式类似 { code: 200, data: ..., msg: success } // type Response struct { // Code int json:code // Message string json:message // Data interface{} json:data // interface{} 类似 TypeScript 的 any // } // // // // 2. 中间件 (Middleware) // // // // LoggerMiddleware 自定义日志中间件 // // 前端视角这非常像 Express 中的 app.use((req, res, next) { ... }) 或者 Koa 的 ctx.middleware。 // // 它在请求到达具体业务逻辑之前执行也可以在响应返回之前执行。 // func LoggerMiddleware() gin.HandlerFunc { // return func(c *gin.Context) { // // 1. 请求前处理 (类似 React useEffect 的依赖项变化前或者 Axios 请求拦截器) // startTime : time.Now() // method : c.Request.Method // path : c.Request.URL.Path // // 打印日志 // fmt.Printf([LOG] 收到请求 - %s %s\n, method, path) // // 2. 传递控制权 (关键类似 next()) // // 如果没有这行代码请求就会在这里中断后面的业务逻辑永远不会执行。 // c.Next() // // 3. 请求后处理 (类似 Axios 响应拦截器或者页面渲染后的回调) // // 此时具体的业务逻辑已经执行完了我们可以记录耗时或修改响应状态 // duration : time.Since(startTime) // statusCode : c.Writer.Status() // fmt.Printf([LOG] 请求结束 - 状态码: %d, 耗时: %v\n, statusCode, duration) // // 注意在 Go Gin 中通常不需要手动发送响应因为业务函数里已经写了 c.JSON() // // 中间件主要负责“旁路”逻辑如日志、鉴权、CORS。 // } // } // // AuthMiddleware 简单的模拟鉴权中间件 // func AuthMiddleware() gin.HandlerFunc { // return func(c *gin.Context) { // // 模拟检查 Header 中是否有 Authorization 字段 // // 前端视角类似前端路由守卫 (Vue Router beforeEach / React Router ProtectedRoute) // token : c.GetHeader(Authorization) // if token { // // 如果没带 Token直接中断请求返回 401 // // c.AbortWithStatusJSON 类似 res.status(401).json(...) 并立即停止后续处理 // c.AbortWithStatusJSON(http.StatusUnauthorized, Response{ // Code: 401, // Message: 未授权访问请携带 Token, // Data: nil, // }) // return // 重要中断后必须 return否则代码会继续往下走 // } // // 如果验证通过可以将用户信息存入上下文 (Context)供后续函数使用 // // 类似把 user 对象挂载到 req.user 上 // c.Set(currentUser, FrontendDeveloper) // // 继续执行 // c.Next() // } // } // // // // 3. 业务处理函数 (Handlers/Controllers) // // // // GetHello 处理首页请求 // // 前端视角这就是一个 API 接口函数。参数 c *gin.Context 包含了 request 和 response 的能力。 // // 它类似于 Express 中的 (req, res) { ... } // func GetHello(c *gin.Context) { // // 获取 URL 查询参数 ?namexxx // // 类似 new URLSearchParams(req.url).get(name) // name : c.DefaultQuery(name, Guest) // // 返回 JSON 响应 // // 类似 res.json({ ... }) // c.JSON(http.StatusOK, Response{ // Code: 200, // Message: 成功, // Data: gin.H{ // gin.H 是 map[string]interface{} 的简写类似 JS 的对象字面量 { key: value } // greeting: fmt.Sprintf(Hello, %s! 欢迎学习 Go 语言。, name), // tech: Gin Framework, // }, // }) // } // // CreateUser 处理创建用户请求 // func CreateUser(c *gin.Context) { // var newUser User // // 参数绑定与验证 // // 前端视角这步非常强大它自动把 Request Body 的 JSON 解析到 newUser 结构体中 // // 并且根据 binding:required 自动验证。如果失败直接报错。 // // 类似 bodyParser.json() 加上 schema 验证库 (如 Zod/Joi)。 // if err : c.ShouldBindJSON(newUser); err ! nil { // // 验证失败返回错误 // c.JSON(http.StatusBadRequest, Response{ // Code: 400, // Message: 参数错误, // Data: err.Error(), // }) // return // } // // 模拟业务逻辑保存用户到数据库... // // 这里我们假装保存成功了把密码去掉再返回 // newUser.Password // c.JSON(http.StatusCreated, Response{ // Code: 201, // Message: 用户创建成功, // Data: newUser, // }) // } // // GetSecretData 需要鉴权的接口 // func GetSecretData(c *gin.Context) { // // 获取中间件中存入的数据 // // 类似读取 req.user // user, exists : c.Get(currentUser) // if !exists { // // 理论上不会走到这里因为中间件已经拦截了但为了安全做个兜底 // c.JSON(http.StatusForbidden, Response{Code: 403, Message: 禁止访问, Data: nil}) // return // } // c.JSON(http.StatusOK, Response{ // Code: 200, // Message: 访问成功, // Data: gin.H{ // secret_info: 这是只有登录用户才能看到的机密数据。, // accessed_by: user, // }, // }) // } // // // // 4. 主函数 (Entry Point) // // // func main() { // // // // 核心概念解读Engine (引擎) // // // // 前端视角 // // 1. 它就像 Express 中的 const app express()。 // // 2. 它更像是一个“总路由器” “插件管理器”。 // // 3. 它负责管理所有的路由表URL 映射到哪个函数、全局中间件、以及配置。 // // // // 为什么叫 Engine // // 因为它驱动了整个 HTTP 服务的运转。它内部维护了一棵“路由树”Radix Tree // // 能极快地根据 URL 找到对应的处理函数。 // // 创建一个默认的 Engine 实例 // // 默认自带了两个中间件Logger (日志) 和 Recovery (崩溃恢复防止服务挂掉) // // 类似const app express(); app.use(logger()); app.use(errorHandler()); // r : gin.Default() // // 注册全局中间件 // // 所有经过这个 engine 的请求都会先过一遍 LoggerMiddleware // r.Use(LoggerMiddleware()) // // // // 路由组 (Router Group) // // // // 前端视角类似 React Router 的 Route path/api/v1 包裹一组子路由 // // 或者 Express 的 const apiRouter express.Router(); app.use(/api, apiRouter); // // 作用给一组路由添加共同的前缀或者应用特定的中间件比如鉴权。 // // 创建一个公开的路由组前缀为 /api/v1 // publicGroup : r.Group(/api/v1) // { // // 定义路由 // // 方法GET, POST, PUT, DELETE 等 // // 路径相对路径会自动加上组前缀 /api/v1 // // 处理函数上面定义的函数名 // publicGroup.GET(/hello, GetHello) // publicGroup.POST(/users, CreateUser) // } // // 创建一个受保护的路由组前缀为 /api/v1/private // // 这个组下的所有路由都会先经过 AuthMiddleware 检查 // privateGroup : r.Group(/api/v1/private) // privateGroup.Use(AuthMiddleware()) // 组级中间件 // { // privateGroup.GET(/secret, GetSecretData) // } // // // // 启动服务 // // // // 前端视角类似 app.listen(8080, () console.log(Server running...)) // fmt.Printf( 服务器正在启动...) // fmt.Printf( 访问地址: http://localhost:8080) // fmt.Printf( 提示按 CtrlC 停止服务) // // 阻塞运行直到服务停止 // // 默认端口 8080 // if err : r.Run(:8080); err ! nil { // fmt.Printf(❌ 启动失败%v\n, err) // } // } // package main // import ( // fmt // time // ) // // 简化版 Context仅用于演示 // type MockContext struct { // Method string // Path string // Status int // } // // 模拟 Next() 行为执行 handler // func (c *MockContext) Next(handler func()) { // handler() // } // // 模拟业务处理函数 // func mockHandler(c *MockContext) { // fmt.Printf( → 执行业务逻辑: 处理 %s %s\n, c.Method, c.Path) // // 模拟设置状态码 // c.Status 200 // time.Sleep(50 * time.Millisecond) // 模拟耗时 // } // // 手动实现 LoggerMiddleware 的核心逻辑不依赖 Gin // func SimpleLoggerMiddleware(c *MockContext, handler func()) { // fmt.Printf([LOG] 收到请求 - %s %s\n, c.Method, c.Path) // startTime : time.Now() // c.Next(handler) // 执行业务逻辑 // duration : time.Since(startTime) // fmt.Printf([LOG] 请求结束 - 状态码: %d, 耗时: %v\n, c.Status, duration) // } // func main() { // fmt.Println( 手动演示中间件执行流程无 Gin 依赖\n) // // 构造一个请求上下文 // ctx : MockContext{ // Method: GET, // Path: /api/v1/hello, // } // // 直接调用中间件包装业务逻辑 // SimpleLoggerMiddleware(ctx, func() { // mockHandler(ctx) // }) // fmt.Println(\n✅ 演示结束清晰看到 请求前 → 业务 → 请求后 的顺序) // } package main import ( fmt time ) // 模拟 Gin 的 Context 和中间件机制 type Context struct { Method string Path string Status int // 存储中间件链和当前执行位置 middlewares []MiddlewareFunc index int finalHandler func(*Context) // 最终业务逻辑 } // MiddlewareFunc 是中间件的标准签名对标 gin.HandlerFunc type MiddlewareFunc func(*Context) // Next 方法Gin 的核心自动调用下一个中间件或最终 handler func (c *Context) Next() { // 如果还有中间件未执行继续执行 if c.index len(c.middlewares) { c.index c.middlewares[c.index-1](c) // 执行当前中间件注意index 已提前 return } // 否则执行最终业务逻辑 if c.finalHandler ! nil { c.finalHandler(c) } } // Use 方法注册中间件模拟 r.Use() func (c *Context) Use(middleware MiddlewareFunc) { c.middlewares append(c.middlewares, middleware) } // 定义中间件完全复刻你的 LoggerMiddleware func LoggerMiddleware() MiddlewareFunc { return func(c *Context) { fmt.Printf([LOG] 收到请求 - %s %s\n, c.Method, c.Path) startTime : time.Now() c.Next() // ← 关键这里会自动触发下一个中间件或业务逻辑 duration : time.Since(startTime) fmt.Printf([LOG] 请求结束 - 状态码: %d, 耗时: %v\n, c.Status, duration) } } // 业务处理函数 func GetHelloHandler(c *Context) { fmt.Printf( → 执行业务逻辑: Hello!\n) c.Status 200 time.Sleep(30 * time.Millisecond) } // 主函数模拟 Gin 启动和请求 func main() { fmt.Println( 模拟 Gin 中间件真实执行流程闭包 c.Next() 自动调度\n) // 创建上下文模拟一次 HTTP 请求 ctx : Context{ Method: GET, Path: /api/v1/hello, finalHandler: GetHelloHandler, } // 注册中间件就像 r.Use(LoggerMiddleware()) ctx.Use(LoggerMiddleware()) // 开始处理请求从第一个中间件开始 ctx.Next() fmt.Println(\n✅ 完美复现 Gin 行为中间件闭包由框架自动调用) }