原则用什么装什么一段代码一个知识点。看完能开干。0. 前置Go 安装参考https://blog.csdn.net/zz00008888/article/details/155709949安装完成后# 官方代理https://proxy.golang.org,directgoenv-wGOPROXYhttps://goproxy.cn,direct# 国内代理goenvGOPROXY# 查看当前代理mkdirmyappcdmyapp go mod init myapp# 初始化模块常用命令go run .运行、go build编译、go mod tidy整理依赖装了新依赖、删了 import、拉了别人代码都跑一次不确定时也跑一次不会出错。新手须知Go 项目里的.go文件都是自己手动创建的包括main.goGo 没有脚手架帮你生成。唯一自动生成的是go mod init产生的go.mod和装依赖后出现的go.sum这俩不用手动改。1. Hello Gin# go get -u github.com/gin-gonic/gin 不带 -u更安全带 -u会升级一大片go get github.com/gin-gonic/gin新建文件main.go项目根目录下自己创建packagemainimportgithub.com/gin-gonic/ginfuncmain(){r:gin.Default()// 自带日志 panic 恢复想要安静无日志用 gin.New()r.GET(/ping,func(c*gin.Context){// gin.H 本质就是 map[string]any写 JSON 返回很方便c.JSON(200,gin.H{msg:pong})})// 不写端口默认 :8080也可以写 :9090 换端口r.Run(:8080)}go run .→ 浏览器访问 http://localhost:8080/ping看到{msg:pong}就成功了。go run .vsgo run main.go都能跑但go run .会编译当前目录所有.go文件项目大了以后多个文件不会漏编译推荐用.。2. 路由 参数// GET /user/42?nametom → id42, nametomr.GET(/user/:id,func(c*gin.Context){id:c.Param(id)// 路径参数取 :id 的值name:c.Query(name)// 查询参数取 ?namexxxname2:c.DefaultQuery(name,默认值)// 取不到就用默认值c.JSON(200,gin.H{id:id,name:name,name2:name2})})// POST 表单提交Content-Type: application/x-www-form-urlencodedr.POST(/user,func(c*gin.Context){form:c.PostForm(name)c.String(200,form)})// 路由分组相同前缀的路由放一起方便管理api:r.Group(/api/v1)// 实际路径/api/v1/helloapi.GET(/hello,hello)常用 HTTP 方法GET查数据、POST提交/新增、PUT整体更新、DELETE删除。对应r.GET()r.POST()r.PUT()r.DELETE()。3. JSON 绑定 参数校验内置无需装typeLoginReqstruct{Userstringjson:user binding:required,min3Pwdstringjson:pwd binding:required,min6}r.POST(/login,func(c*gin.Context){varreq LoginReqiferr:c.ShouldBindJSON(req);err!nil{c.JSON(400,gin.H{err:err.Error()})return}c.JSON(200,gin.H{ok:true})})Tag 是什么就是结构体字段后面反引号里的标注告诉框架怎么处理这个字段json:user—— JSON 序列化/反序列化时用user作为字段名不写就用大写字段名binding:required—— Gin 的校验规则表示该字段必填常用 binding 校验 tagtag含义示例required必填binding:requiredmin/max字符串最小/最大长度binding:min3,max20gte/lte数字 ≥ / ≤binding:gte1,lte100email必须是邮箱格式binding:emailoneof只能是指定值之一binding:oneofmale femaleomitempty空值时跳过校验binding:omitempty,min34. 中间件3 行就能写funcAuth()gin.HandlerFunc{returnfunc(c*gin.Context){ifc.GetHeader(Authorization){c.AbortWithStatusJSON(401,gin.H{err:no token})return}c.Next()}}r.Use(Auth())// 全局api.Use(Auth())// 分组r.GET(/x,Auth(),xFn)// 单个中间件执行顺序c.Next()之前的代码在请求进来时执行前置c.Next()之后的代码在响应返回时执行后置。c.Abort()会直接中断不再往下走。跨域需要时go get github.com/gin-contrib/corsimportgithub.com/gin-contrib/corsr.Use(cors.Default())5. 连接 MySQLGORMgo get gorm.io/gorm gorm.io/driver/mysqlimport(gorm.io/driver/mysqlgorm.io/gorm)varDB*gorm.DBfuncinitDB(){// 格式用户名:密码tcp(地址:端口)/数据库名?参数// 注意数据库 myapp 要先在 MySQL 里手动创建好GORM 不会帮你建库dsn:root:123456tcp(127.0.0.1:3306)/myapp?charsetutf8mb4parseTimeTruelocLocalvarerrerrorDB,errgorm.Open(mysql.Open(dsn),gorm.Config{})iferr!nil{panic(err)// 连不上就直接挂别吞错误不然后面全是空指针}}// 结构体 表字段 列gorm tag 告诉 GORM 怎么建表typeUserstruct{IDuintgorm:primaryKey// primaryKey 主键Namestringgorm:size:64// size:64 varchar(64)}// 增删改查放在任意函数里用不能直接写在包顶层funcdemoCRUD(){DB.AutoMigrate(User{})// 建表/加字段DB.Create(User{Name:a})// 增varu User DB.First(u,1)// 按主键查DB.Where(name ?,a).Find(u)// 按条件查DB.Model(u).Update(name,b)// 改DB.Delete(u)// 删}6. 表结构反向生成模型GORM Gen已建表不想手写字段。go get gorm.io/gen新建文件scripts/gen/main.go需要先创建scripts/gen/目录packagemainimport(gorm.io/driver/mysqlgorm.io/gengorm.io/gorm)funcmain(){db,_:gorm.Open(mysql.Open(root:123456tcp(127.0.0.1:3306)/myapp?charsetutf8mb4parseTimeTruelocLocal),gorm.Config{})g:gen.NewGenerator(gen.Config{OutPath:./internal/query,ModelPkgPath:./internal/model,})g.UseDB(db)g.ApplyBasic(g.GenerateAllTable()...)// 所有表g.Execute()}go run ./scripts/gen使用类型安全、无字符串q:query.Use(DB)u,_:q.User.Where(q.User.Name.Eq(a)).First()表结构变了就重跑一次。7. RedisRedis 是内存数据库常用来做缓存、Session 存储、排行榜等。需要先在本地装好 Redis 服务并启动。go get github.com/redis/go-redis/v9import(timegithub.com/redis/go-redis/v9)varRDB*redis.ClientfuncinitRedis(){RDBredis.NewClient(redis.Options{Addr:127.0.0.1:6379,// Redis 地址Password:,// 没设密码就留空DB:0,// 默认用 0 号库})}// 在 Handler 里使用c 是 *gin.ContextfuncdemoRedis(c*gin.Context){ctx:c.Request.Context()RDB.Set(ctx,k,v,10*time.Minute)// 写入10 分钟过期val,_:RDB.Get(ctx,k).Result()// 读取RDB.Del(ctx,k)// 删除RDB.SetNX(ctx,lock,1,5*time.Second)// 不存在才写入可做简单锁_val}8. 配置文件Vipergo get github.com/spf13/viper新建文件config.yaml项目根目录下自己创建port:8080mysql_dsn:root:123456tcp(127.0.0.1:3306)/myapp?charsetutf8mb4parseTimeTruelocLocalimportgithub.com/spf13/viperfuncinitConfig(){viper.SetConfigFile(config.yaml)iferr:viper.ReadInConfig();err!nil{panic(err)// 配置读不到没法跑直接挂}port:viper.GetInt(port)// 取 intdsn:viper.GetString(mysql_dsn)// 取 string_port_dsn}9. JWT 鉴权go get github.com/golang-jwt/jwt/v5import(timegithub.com/gin-gonic/gingithub.com/golang-jwt/jwt/v5)// secret 是签名密钥生产环境别硬编码放配置文件里varsecret[]byte(my-secret)// 签发登录成功后调用把返回的 token 给前端funcsignToken(uidint)(string,error){returnjwt.NewWithClaims(jwt.SigningMethodHS256,jwt.MapClaims{uid:uid,// 自定义数据一般放用户 IDexp:time.Now().Add(24*time.Hour).Unix(),// 过期时间戳}).SignedString(secret)}// 校验放中间件里前端请求头带 Authorization: Bearer tokenfuncJWTAuth()gin.HandlerFunc{returnfunc(c*gin.Context){raw:c.GetHeader(Authorization)// 从请求头拿 tokent,err:jwt.Parse(raw,func(*jwt.Token)(interface{},error){returnsecret,nil})iferr!nil||!t.Valid{c.AbortWithStatusJSON(401,gin.H{err:invalid token})return}uid:t.Claims.(jwt.MapClaims)[uid]// 从 token 里取出用户 IDc.Set(uid,uid)// 丢到上下文后面 Handler 用 c.Get(uid) 取c.Next()}}10. 定时任务go get github.com/robfig/cron/v3importgithub.com/robfig/cron/v3c:cron.New(cron.WithSeconds())// 秒级c.AddFunc(*/10 * * * * *,func(){log.Println(每10秒)})c.AddFunc(0 0 3 * * *,func(){log.Println(每天3点)})c.Start()表达式秒 分 时 日 月 周*表示每、*/10表示每隔 10、0表示固定值。不加cron.WithSeconds()时是 5 段分 时 日 月 周加了才是 6 段最前面多一列秒别写错段数会启动报错。11. 文件上传// 注意需要先手动创建 uploads 目录否则保存会报错r.POST(/upload,func(c*gin.Context){f,_:c.FormFile(file)c.SaveUploadedFile(f,./uploads/f.Filename)c.JSON(200,gin.H{name:f.Filename})})12. 统一响应推荐写一个每个接口都写c.JSON(200, gin.H{...})太散封装两个函数全项目统一格式。funcok(c*gin.Context,data any){c.JSON(200,gin.H{code:0,data:data})}funcfail(c*gin.Context,codeint,msgstring){c.JSON(200,gin.H{code:code,msg:msg})}13. 热重载开发每次改代码都要手动go run .太烦装个 Air改完代码自动重新编译运行。goinstallgithub.com/air-verse/airlatest# 让终端找得到 air 命令仅当前终端生效exportPATH$PATH:$(goenvGOPATH)/bin air init# 生成 .air.toml 配置文件自动生成不用手动改air# 启动热重载代替 go run .之后开发就用air代替go run .改完代码保存终端会自动重启服务。永久生效把上面那行export PATH...追加到~/.zshrcMac 默认或~/.bashrc执行source ~/.zshrc立即生效之后开新终端也能直接用air。14. 编译上线go build会把代码编译成一个可执行文件丢到服务器上直接运行不需要装 Go 环境。# 本地编译生成当前系统的可执行文件go build-obin/app.# 打 Linux 包Mac/Win 上交叉编译部署到 Linux 服务器用CGO_ENABLED0GOOSlinuxGOARCHamd64 go build-obin/app-linux.最小 Dockerfile# 第一阶段编译。用官方 Go 镜像把代码打成一个二进制 FROM golang:1.23-alpine AS builder WORKDIR /src COPY . . RUN CGO_ENABLED0 go build -o /app . # 第二阶段运行。只拷二进制和配置镜像体积极小 FROM alpine COPY --frombuilder /app /app COPY config.yaml /config.yaml ENTRYPOINT [/app]15. 请求完整流程一图记住请求 → 全局中间件 → 分组中间件 → Handler (日志/跨域) (JWT/限流) ↓ 绑定参数 ShouldBindJSON ↓ Service 业务逻辑 ↓ GORM / Redis ↓ 统一 JSON 返回16. 插件速查表需求装什么说明Web 框架github.com/gin-gonic/gin本手册核心MySQLgorm.io/gormgorm.io/driver/mysqlORM不用写 SQLPostgreSQLgorm.io/gormgorm.io/driver/postgres换数据库只换驱动SQLitegorm.io/gormgorm.io/driver/sqlite轻量本地数据库学习测试用反向生成模型gorm.io/gen已有表结构 → Go 结构体Redisgithub.com/redis/go-redis/v9缓存/Session配置文件github.com/spf13/viperyaml/json/toml 都支持JWTgithub.com/golang-jwt/jwt/v5无状态登录鉴权跨域github.com/gin-contrib/cors前后端分离必备定时任务github.com/robfig/cron/v3秒级定时日志go.uber.org/zap高性能结构化日志热重载github.com/air-verse/air开发阶段自动重启超时github.com/gin-contrib/timeout请求超时控制不是限流参数翻译github.com/go-playground/universal-translator校验错误中文化Swagger 文档github.com/swaggo/gin-swagger自动生成 API 文档WebSocketgithub.com/gorilla/websocket实时通信/聊天密码加密golang.org/x/crypto/bcrypt密码哈希别明文存17. 项目目录参考刚开始可以全写main.go里代码多了再拆。下面是常见的拆法myapp/ ├── main.go # 入口初始化和启动 ├── config.yaml # 配置文件 ├── go.mod / go.sum # 依赖自动生成 ├── router/ # 路由定义 │ └── router.go ├── handler/ # 处理函数接收请求、返回响应 │ └── user.go ├── service/ # 业务逻辑 │ └── user.go ├── model/ # 数据库模型结构体 │ └── user.go ├── middleware/ # 中间件 │ └── auth.go └── utils/ # 工具函数 └── response.go18. 常踩的坑连不上 MySQL检查端口、密码Docker 里别写127.0.0.1要用容器名。中文乱码 / 时间差 8 小时DSN 带charsetutf8mb4parseTimeTruelocLocal。go: cannot find main module没go mod init或不在项目目录。air: command not found把$(go env GOPATH)/bin加到 PATH。panic 整个服务挂用gin.Default()或自己挂 Recovery。引入了包但没用Go 不允许删掉或用_占位import _ xxx。结构体字段首字母小写外部包访问不到JSON 也序列化不出来。Go 里大写 公开小写 私有。go get报网络错检查代理go env GOPROXY国内用https://goproxy.cn,direct。端口被占用lsof -i :8080查谁在用换个端口或杀掉进程。syntax error: non-declaration statement outside function body变量赋值、函数调用这类语句直接写在包顶层了要包进func里才能执行。开干顺序建议第 1 节跑通 → 第 2/3 节熟悉路由和参数 → 第 5 节连库 → 第 7 节接 Redis → 第 4/9 节加中间件和登录 → 第 10 节加定时任务 → 第 13 节换 air 开发 → 第 14 节打包上线。搞定。