1.安装Gin

go mod init 项目名
go get -u github.com/gin-gonic/gin

2.最简单的服务器

// 创建一个路由引擎
router := gin.Default()
// 配置路由
router.GET("/", func(ctx *gin.Context) {
	ctx.String(200, "值:%v", "你好,Gin")
})
// 默认8080服务
router.Run(":8000") // 启动 web 8000服务

3.安装热加载air

go get -tool github.com/air-verse/air@latest
go tool air // 执行air

4.json

type Article struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}

type Response[T any] struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data T      `json:"data"`
}

// 方式一 不推荐
router.POST("/json", func(ctx *gin.Context) {
	ctx.JSON(200, map[string]any{
		"code": 200,
		"data": "ok",
	})
})
// 方式二 不推荐
router.POST("/json2", func(ctx *gin.Context) {
	ctx.JSON(200, gin.H{
		"code": 200,
		"data": "ok",
	})
})
// 方式三 推荐
router.POST("/json3", func(ctx *gin.Context) {
	ctx.JSON(200, &Response[*Article]{
		Code: 200,
		Msg:  "",
		Data: &Article{
			Title:   "标题",
			Content: "内容",
		},
	})
})
/* 打印
{
    "code": 200,
    "msg": "",
    "data": {
        "title": "标题",
        "content": "内容"
    }
}
*/

5.将数据绑定到结构体 方式一:

// GET 获取query数据
ctx.Query("xx")
ctx.DefaultQuery("xx","")
// POST 获取form数据
ctx.PostForm("")
ctx.DefaultPostForm("xx", "")
// POST 获取raw数据
ctx.GetRawData()
// 动态路由
ctx.Param("uid")

方式二:

type User struct {
	Name string `form:"name" json:"name"`
	// 解决前端传类型不规范问题,一会儿传 20(数字),一会儿传 "20"(字符串)
	Age json.Number `form:"age" json:"age"`
}

router.POST("/json2", func(ctx *gin.Context) {
	user := &User{}
	// 自动绑定:无论前端传的是 Form 数据还是 JSON(Raw) 数据
	// 这一句都能自动解析并转换类型!
	ctx.ShouldBind(&user)
	fmt.Println("绑定到结构体:", user)
	ctx.JSON(200, map[string]any{
		"code": 200,
		"data": "ok",
	})
})

6.路由分组

apiRoutes := router.Group("/api")
{
	apiRoutes.POST("/add", func(ctx *gin.Context) {
		ctx.String(200, "api添加")
	})
	apiRoutes.POST("/update", func(ctx *gin.Context) {
		ctx.String(200, "api修改")
	})
}

adminRoutes := router.Group("/admin")
{
	adminRoutes.POST("/add", func(ctx *gin.Context) {
		ctx.String(200, "admin添加")
	})
	adminRoutes.POST("/update", func(ctx *gin.Context) {
		ctx.String(200, "admin修改")
	})
	}

7.抽离router admin.go

package routers

import "github.com/gin-gonic/gin"

func AdminInit(router *gin.Engine) {
	adminRoutes := router.Group("/admin")
	{
		adminRoutes.POST("/add", func(ctx *gin.Context) {
			ctx.String(200, "admin添加")
		})
		adminRoutes.POST("/update", func(ctx *gin.Context) {
			ctx.String(200, "admin修改")
		})
	}
}

api.go

package routers

import "github.com/gin-gonic/gin"

func ApiInit(router *gin.Engine) {
	apiRoutes := router.Group("/api")
	{
		apiRoutes.POST("/add", func(ctx *gin.Context) {
			ctx.String(200, "api添加")
		})
		apiRoutes.POST("/update", func(ctx *gin.Context) {
			ctx.String(200, "api修改")
		})
	}
}

main.go

router := gin.Default()
routers.ApiInit(router) // 初始化api
routers.AdminInit(router) // 初始化admin

8.抽离controller admin.go

package routers

import (
	"gindemo/controllers/admin"

	"github.com/gin-gonic/gin"
)

func AdminInit(router *gin.Engine) {
	adminRoutes := router.Group("/admin")
	{
		adminRoutes.POST("/user/add", admin.UserController{}.UserAdd)
		adminRoutes.POST("/user/update", admin.UserController{}.UserUpdate)
		adminRoutes.POST("/article/add", admin.ArticleController{}.ArticleAdd)
		adminRoutes.POST("/article/update", admin.ArticleController{}.ArticleUpdate)
	}
}

admin文件夹下面新建两个文件,userController.go 和 articleController.go userController.go

package admin

import "github.com/gin-gonic/gin"

type UserController struct{}

func (con UserController) UserAdd(ctx *gin.Context) {
	ctx.String(200, "admin user添加")
}

func (con UserController) UserUpdate(ctx *gin.Context) {
	ctx.String(200, "admin user修改")
}

articleController.go

package admin

import "github.com/gin-gonic/gin"

type ArticleController struct{}

func (con ArticleController) ArticleAdd(ctx *gin.Context) {
	ctx.String(200, "admin article添加")
}

func (con ArticleController) ArticleUpdate(ctx *gin.Context) {
	ctx.String(200, "admin article修改")
}

8.中间件

func initMiddleware(ctx *gin.Context) {
	fmt.Println("请求前")
	ctx.Next()
	fmt.Println("请求结束")
}

adminRoutes.POST("/article/add", initMiddleware, admin.ArticleController{}.ArticleAdd)

func (con ArticleController) ArticleAdd(ctx *gin.Context) {
	fmt.Println("请求中")
	ctx.String(200, "admin article添加")
}
/*输出 请求前,请求中,请求结束*/

判断用户是否登录

func InitMiddleware(ctx *gin.Context) {
	ctx.Set("token", "1") // 设置token
	fmt.Println("请求前")
	ctx.Next()
	fmt.Println("请求结束")
}

adminRoutes.POST("/article/add", middlewares.InitMiddleware, admin.ArticleController{}.ArticleAdd)

func (con ArticleController) ArticleAdd(ctx *gin.Context) {
	token, _ := ctx.Get("token") // 获取token
	if token != "" {
		fmt.Println("已登录")
	} else {
		fmt.Println("未登录")
	}
	ctx.String(200, "admin article添加")
}
/*输出 请求前,已登录,请求结束*/

9.单文件上传和多文件上传 // 单文件

func (con UserController) UserUpload(ctx *gin.Context) {
	// 获取前端上传的avator图片
	file, err := ctx.FormFile("avator")
	dst := path.Join("./upload", file.Filename)
	if err == nil {
		// 保存到当前项目的upload文件夹
		ctx.SaveUploadedFile(file, dst)
	}
	fmt.Println(dst)
	ctx.String(200, "admin user单文件上传")
}

// 多文件

func (con UserController) UserMultipleUpload(ctx *gin.Context) {
	// 获取前端上传的avators图片
	form, _ := ctx.MultipartForm()
	files, ok := form.File["avators[]"]
	if ok {
		// 保存到当前项目的upload文件夹
		for _, file := range files {
			dst := path.Join("./upload", file.Filename)
			ctx.SaveUploadedFile(file, dst)
		}
	}
	ctx.String(200, "admin user多文件上传")
}

10.设置cookie

func (con UserController) UserAdd(ctx *gin.Context) {
    // 设置/删除cookie,第三个参数 -1 为删除
	ctx.SetCookie("token", "3271DF3DEAEB65C53EF9C848EB7964B7", 3600, "/", "localhost", false, false)
	ctx.String(200, "admin user添加")
}

func (con UserController) UserUpdate(ctx *gin.Context) {
    // 获取token
	cookie, _ := ctx.Cookie("token")
	fmt.Println("cookie----", cookie) // 3271DF3DEAEB65C53EF9C848EB7964B7
	ctx.String(200, "admin user修改")
}

11.设置session session保存在服务器,cookie保存在浏览器

创建基于cookie的store

// 安装session
go get github.com/gin-contrib/sessions

1.配置session中间件
import {
    "github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	...
}

router := gin.Default()
// 创建基于cookie的store,加密的密钥
store := cookie.NewStore([]byte("secret"))
// 配置session中间件
router.Use(sessions.Sessions("mysession", store))

2.设置session
session := sessions.Default(ctx)
session.Set("soken", "3271DF3DEAEB65C53EF9C848EB7964B7")
session.Save()

3.获取session
session := sessions.Default(ctx)
soken := session.Get("soken")
fmt.Println("session----", soken)

创建基于redis的store

import {
    ...
    "github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/redis"
}

// 创建基于redis的store,加密的密钥
/* 	六个参数:
size=10:连接池大小
network="tcp"
address="localhost:6379"
username="":Redis 无用户名认证时留空
password="":Redis 无密码时留空(如果你的 Redis 设置了密码,填在这里)
keyPairs=[]byte("secret"):真正用于 session 加密签名的密钥
*/

store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", "", []byte("secret"))
// 配置session中间件
router.Use(sessions.Sessions("mysession", store))

// 设置session过期时间
session.Options(sessions.Options{
	MaxAge: 3600 * 6, // 6hrs
})

12.mysql常用命令

// 查看数据库
show databases;

// 选择数据库
use beego;

// 查看表
show tables;

// 查看表字段
describe users;

// 简单查询
select * from users;
// 单个字段查询
select username from users;
// 简单条件查询
select * from users where id = 1;

// 创建数据库
create database books;

// 删除数据库
drop database books;

// 创建表
create table users(
    id int(11),
    name varchar(255),
    age int(3),
    PRIMARY KEY (id)
);

// 删除表
drop table users;

// 添加数据
insert into users(name, age) values ("ww", 33);

// 修改数据
update users set id = 1 where name = "ww";

// 删除数据
delete from users where id = 1;

// 排序,升序,降序desc
select * from users order by id desc;

// 统计数量
select count(1) from users;

// 分页limit
select * from users limit 2; //
select * from users limit 1, 2; //跳过第1个,取2

13.mysql常用

整数型:tinyint,smallint,int,bigint
浮点型:float,double,decimal(m,d)
字符型:char,varchar
备注型:tinytext,text,longtext

where表达式常用运算符:=,>,<,>=,<=,!=,is not null,is null,between,not between,in,or,and,not,like "%x%"
分组函数:avg,count,max,min,sum
别名:`as`

内连接:inner join...on...
左外连接:left join...on...
右边连接:right join...on...

索引(提升查询速度):普通索引,唯一索引,(全文索引,空间索引 了解即可)
创建索引:
alter table 表名 add index 索引名(列名);
删除索引:
alter table 表名 drop index 索引名;
查看索引:
show index from 表名;

14.mysql事务和锁 事务

begin; -- 开启事务

update users set age = age - 40 where id = 563;
update users set age = age + 40 where id = 2490631;

commit; -- 事务确认
-- rollback; -- 事务回滚

锁,表级锁,行级锁

-- 添加读锁,可以并发读,不能并发写
lock table users read; --加读锁
unlock tables; --解锁

-- 添加写锁,只能一个人写
lock table users write;  --加写锁
unlock tables; --解锁

15.gorm库1.安装 go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite

2.init中初始化gorm对象 core.go

package models

import (
	"fmt"
	"reflect"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func init() {
	dsn := "root:@tcp(127.0.0.1:3306)/books?charset=utf8mb4&parseTime=True&loc=Local"
	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("数据库连接成功")
		fmt.Println(reflect.ValueOf(DB), reflect.TypeOf(DB))
	}
}

3.创建表及操作的models 创建user表

create table user(
    id int(11) AUTO_INCREMENT,
    username varchar(255),
    age tinyint,
    email varchar(255),
    add_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

insert into user(username,age,email) values("ww", 33, "test1@gmail.com");

得到该数据:
+----+----------+------+-----------------+---------------------+
| id | username | age  | email           | add_time            |
+----+----------+------+-----------------+---------------------+
|  1 | ww       |   33 | test1@gmail.com | 2026-09-19 10:42:57 |
+----+----------+------+-----------------+---------------------+

声明user的models

package models

import (
	"encoding/json"
	"time"

	"gorm.io/gorm"
)

type User struct {
	Id       int       `json:"id"`
	Username string    `json:"name"`
	Age      int       `json:"age"`
	Email    string    `json:"email"`
	AddTime  time.Time `json:"-"` // 数据库为 DATETIME 类型(dsn 开启了 parseTime),需用 time.Time 接收,否则 Scan 类型不匹配导致该字段恒为 0
}

func (User) TableName() string {
	// 自定义表名
	return "user"
}

// BeforeCreate GORM 钩子:插入前自动填充创建时间,避免调用方遗漏赋值导致 AddTime 为零值
func (u *User) BeforeCreate(tx *gorm.DB) error {
	if u.AddTime.IsZero() {
		u.AddTime = time.Now()
	}
	return nil
}

// MarshalJSON 将 AddTime 以 Unix 时间戳的形式返回给前端
func (u User) MarshalJSON() ([]byte, error) {
	type Alias User
	return json.Marshal(&struct {
		Time int64 `json:"time"`
		Alias
	}{
		Time:  u.AddTime.Unix() * 1000,
		Alias: Alias(u),
	})
}

声明响应Response的struct:

package controllers

type Response[T any] struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data T      `json:"data"`
}

查询数据:

func (con UserController) List(ctx *gin.Context) {
	// 查询数据
	userList := []models.User{}
	// 前三条
	models.DB.Limit(3).Find(&userList)
	// age 大于20
	models.DB.Where("age >= ?", 20).Find(&userList)
	// 第一条
	models.DB.First(&userList)
	ctx.JSON(200, &Response[*[]models.User]{
		Code: 200,
		Msg:  "",
		Data: &userList,
	})
}

数据返回:
{
    "code": 200,
    "msg": "",
    "data": [
        {
            "time": 1789785777000,
            "id": 1,
            "name": "ww",
            "age": 33,
            "email": "test1@gmail.com"
        }
    ]
}

新增数据:

func (con UserController) Add(ctx *gin.Context) {
	// 创建数据
	user := models.User{
		Username: "vv",
		Age:      40,
		Email:    "test2@gmail.com",
	}
	models.DB.Create(&user)
	ctx.JSON(200, &Response[string]{
		Code: 200,
		Msg:  "",
		Data: "ok",
	})
}

数据返回:
{
    "code": 200,
    "msg": "",
    "data": [
        {
            "time": 1789785777000,
            "id": 1,
            "name": "ww",
            "age": 33,
            "email": "test1@gmail.com"
        },
        {
            "time": 1789789724000,
            "id": 2,
            "name": "vv",
            "age": 40,
            "email": "test2@gmail.com"
        }
    ]
}

修改数据:

func (con UserController) Edit(ctx *gin.Context) {
	// 修改数据
	user := models.User{}
	models.DB.Where("id = ?", 2).Find(&user)
	user.Age = 8
	models.DB.Save(&user)
	ctx.JSON(200, &Response[string]{
		Code: 200,
		Msg:  "",
		Data: "ok",
	})
}

删除数据:

func (con UserController) Delete(ctx *gin.Context) {
    // 修改数据
	user := models.User{}
	models.DB.Where("id = ?", 2).Delete(&user)
	ctx.JSON(200, &Response[string]{
		Code: 200,
		Msg:  "",
		Data: "ok",
	})
}

where条件查询例子:

// ==
db.Where("name = ?", "jinzhu").First(&user)

// !=
db.Where("name <> ?", "jinzhu").Find(&users)

// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)

// Time
db.Where("updated_at > ?", lastWeek).Find(&users)

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)

原生语句:

// 增、删、改
db.Exec("DELETE from users WHERE id = ?",1)

// 查
db.Raw("SELECT * FROM users WHERE id = ?", 1).Scan(&result)

表的关联查询: 一对一

// User 有一张 CreditCard,UserID 是外键
type User struct {
  CreditCard CreditCard
}

type CreditCard struct {
  Number string
  UserID uint
}

func GetAll(db *gorm.DB) ([]User, error) {
    var users []User
    db.Preload("CreditCard").Find(&users)
    fmt.println(users)
}

一对多

// User 有多张 CreditCard,UserID 是外键
type User struct {
  CreditCards []CreditCard
}

type CreditCard struct {
  Number string
  UserID uint
}

// 检索用户列表并预加载信用卡
func GetAll(db *gorm.DB) ([]User, error) {
    var users []User
    db.Preload("CreditCards").Find(&users)
    fmt.println(users)
}

多对多

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  Name string
}

// 检索 User 列表并预加载 Language
func GetAllUsers(db *gorm.DB) ([]User, error) {
    var users []User
    db.Preload("Languages").Find(&users)
    fmt.println(users)
}

// 检索 Language 列表并预加载 User
func GetAllLanguages(db *gorm.DB) ([]Language, error) {
    var languages []Language
    db.Preload("Users").Find(&languages)
    fmt.println(languages)
}