这篇写得挺扎实,但代码贴到 AuthRequired 那一段就断了——函数体没跟上来,后面「绑定与验证」也还没写到,顺手把这两块补上,正好是新手最容易翻车的地方。

中间件部分补全大概是这样:func AuthRequired() gin.HandlerFunc { return func(c *gin.Context) { token := c.GetHeader("Authorization"); if token == "" { c.AbortWithStatusJSON(401, gin.H{"msg":"unauthorized"}); return }; c.Set("uid", 123); c.Next() } }。关键点是 c.Next() 和 c.Abort() 的区别——不调 Next() 就 return,后面的 handler 不会执行;但更稳妥的是显式 Abort(),否则如果有多个中间件,链条行为容易和你预期不一致。另外如果中间件里要起 goroutine 做异步日志,必须用 c.Copy(),直接传 c 会读到已经归还池子的对象,偶发脏数据。

绑定和验证这块,ShouldBindJSON 和 BindJSON 一定要分清:前者返回 error 由你自己决定响应格式,后者内部直接写 400 并 Abort,前后端联调时返回体不受控。tag 写成 binding:"required,email" 就行,但它对零值判定很坑——bool 传 false、int 传 0 都会判定为「没传」,字段要用指针类型 *int 或者加 omitempty 绕开。想返回中文错误,需要自己注册 ut.Translator,Gin 默认吐的是英文原文。

最后提一个上线前必做的:gin.Default() 自带 Logger 和 Recovery,压测时那套日志 IO 很吃性能,gin.SetMode(gin.ReleaseMode) 加 gin.New() 自选中间件是基操。还有路由冲突那个坑你提到了,补充一句——它是在 Run() 时 panic 而不是编译期,CI 里最好加一条"启动即探测"的冒烟测试,不然要等上线才发现。