1. GORM

1.1 什么是ORM

ORM是object relational mapping就是对象映射关系程序简单的说类似python这种面向对象的程序来说,一切都是对象.为了保证一致性的使用习惯,通过orm将编程语言的对象模型和数据库的关系型模型建立映射关系这样我们直接使用编程语言的对象模型进行数据库操作就可以了.而不是直接使用sql语言.

1.2 什么是GORM

GORM是Golang ORM库

全功能 ORM关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)Create,Save,Update,Delete,Find 中钩子方法支持 Preload、Joins 的预加载事务,嵌套事务,Save Point,Rollback To Saved PointContext、预编译模式、DryRun 模式批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUDSQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询复合主键,索引,约束Auto Migration自定义 Logger灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…每个特性都经过了测试的重重考验开发者友好

1.3 初始化项目

mkdir day04

cd day04

go mod init day04

1.4 安装GORM

go get -u gorm.io/gorm

go get -u gorm.io/driver/sqlite

go get -u gorm.io/driver/mysql

2. 创建数据库

docker run --name mysql -itd -h mysql-server -e MYSQL_ROOT_PASSWORD=root -v /data/mysql5.7:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.34 --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci

root@Ubuntu-1:/data# docker exec -it 0af13655 bash

root@mysql-server:/# mysql -uroot -p123456

mysql: [Warning] Using a password on the command line interface can be insecure.

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 5

Server version: 5.7.34 MySQL Community Server (GPL)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database test_db charset utf8;

Query OK, 1 row affected (0.00 sec)

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| mysql |

| performance_schema |

| sys |

| test_db |

+--------------------+

5 rows in set (0.00 sec)

3. 连接数据库

先要确认数据库连接正常.

package main

import (

"fmt"

"gorm.io/driver/mysql"

"gorm.io/gorm"

)

func main() {

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

if err != nil {

panic(err) // 异常处理

}

fmt.Println("db:", db)

结果

db: &{0xc0001245a0 0 0xc0001d2380 1}

err结果是nil就说明连接正常.

如果失败会报以下错误

[error] failed to initialize database, got error Error 1045 (28000): Access denied for user 'root'@'192.168.31.1' (using password:

YES)

4. 创建表

// 1. 定义字段

type User struct {

// gorm:"primary_key" 主键

Id int64 `json:"id" gorm:"primary_key"`

Username string `json:"username" gorm:"username"`

Password string `json:"password" gorm:"password"`

}

func main() {

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

if err != nil {

panic(err)

}

// 2. 创建表

// 迁移 https://gorm.io/zh_CN/docs/migration.html

db.AutoMigrate(User{})

}

mysql> desc users;

+----------+------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |

+----------+------------+------+-----+---------+----------------+

| id | bigint(20) | NO | PRI | NULL | auto_increment |

| username | longtext | YES | | NULL | |

| password | longtext | YES | | NULL | |

+----------+------------+------+-----+---------+----------------+

3 rows in set (0.00 sec)

运行后该表就被创建了

5. 增删改查

5.1 增

db.Create(&User{

Username: "zhangsan",

Password: "123456",

})

mysql> select * from users;

+----+----------+----------+

| id | username | password |

+----+----------+----------+

| 1 | zhangsan | 123456 |

+----+----------+----------+

1 row in set (0.00 sec)

5.2 改

// 5. 修改字段

db.Model(User{

Id: 1,

}).Update("password", "10JQKA")

mysql> select * from users;

+----+----------+----------+

| id | username | password |

+----+----------+----------+

| 1 | zhangsan | 10JQKA |

+----+----------+----------+

1 row in set (0.00 sec)

5.3 查询

再增加一条数据

mysql> select * from users;

+----+----------+----------+

| id | username | password |

+----+----------+----------+

| 1 | zhangsan | 10JQKA |

| 2 | lisi | 111111 |

+----+----------+----------+

2 rows in set (0.00 sec)

查询一条数据

// 查询

// 过滤单挑数据

u := User{Id: 2}

db.First(&u)

fmt.Printf("%#v\n", u)

// 查询所有数据

users := []User{}

db.Find(&users)

fmt.Printf("%#v\n", users)

结果

main.User{Id:2, Username:"lisi", Password:"111111"}

[]main.User{main.User{Id:1, Username:"zhangsan", Password:"10JQKA"}, main.User{Id:2, Username:"lisi", Password:"111111"}}

5.4 删除

// 删除数据

db.Delete(&User{Id: 1})

db.Find(&users)

fmt.Printf("%#v\n", users)

// 条件删除

db.Where("Username = ?", "lisi").Delete(&User{})

db.Find(&users)

fmt.Printf("%#v\n", users)

结果

[]main.User{main.User{Id:2, Username:"lisi", Password:"111111"}}

[]main.User{}

6. 模型类型

6.1 数据类型

参数含义paimary_key主键string默认字符串对应的是text文本类型*time.Timedatatimevarchar(100)将默认string设置成varchar(100)unique_index唯一索引size:255设置字段大小255个字节not null不为空unique字段唯一AUTO_INCREMENT自增index:addr创建一个名字是addr的索引gorm:“-”忽略这个字段

先创建表

type User struct {

Id int64 `gorm:"paimary_key" json:"id"`

Name string

CreateAt *time.Time `json:"createAt" gorm:"column:create_at"`

Email string `gorm:"type:varchar(100);unique_index"`

Role string `gorm:"size:255"`

MemberNumber int `gorm:"unique;not null"`

Num int `gorm:"AUTO_INCREMENT"`

Address string `gorm:"index:addr"`

IgnoreMe int `gorm:"-"`

}

func main() {

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

db.AutoMigrate(User{})

}

mysql> desc users;

+---------------+--------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |

+---------------+--------------+------+-----+---------+----------------+

| id | bigint(20) | NO | PRI | NULL | auto_increment |

| name | longtext | YES | | NULL | |

| create_at | datetime(3) | YES | | NULL | |

| email | varchar(100) | YES | | NULL | |

| role | varchar(255) | YES | | NULL | |

| member_number | bigint(20) | NO | UNI | NULL | |

| num | bigint(20) | YES | | NULL | |

| address | varchar(191) | YES | MUL | NULL | |

+---------------+--------------+------+-----+---------+----------------+

8 rows in set (0.00 sec)

6.2 支持结构标签

标签名说明column指定 db 列名type列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null、size, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENTserializer指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtimesize定义列数据类型的大小或长度,例如 size: 256primaryKey将列定义为主键unique将列定义为唯一键default定义列的默认值precision指定列的精度scale指定列大小not null指定列为 NOT NULLautoIncrement指定列为自动增长autoIncrementIncrement自动步长,控制连续记录之间的间隔embedded嵌套字段embeddedPrefix嵌入字段的列名前缀autoCreateTime创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nanoautoUpdateTime创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milliindex根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情uniqueIndex与 index 相同,但创建的是唯一索引check创建检查约束,例如 check:age > 13,查看 约束 获取详情<-设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限->设置字段读的权限,->:false 无读权限-忽略该字段,- 表示无读写,-:migration 表示无迁移权限,-:all 表示无读写迁移权限comment迁移时为字段添加注释

7. 一对多关联查询

通过外键实现两张表之间的关联.

type User struct {

gorm.Model

CreditCards []CreditCard

}

type CreditCard struct {

gorm.Model

Number string

UserID uint // 默认会在CreditCard表中生成UserId字段作为User表中关联的外键ID

}

7.1 外键

为了定义一对多关系,外键必须存在,默认外键的名字是所有者类型的名字加上他的主键.就像上面的例子,为了定义一个属于User的模型,外键就应该为UserID使用其他的字段名字作为外键,可以通过foreignkey来定制它

type User struct {

gorm.Model

CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`

}

7.1.1 建立一对多表

// 2. 创建一对多

// 2.1 定义User表

type User struct {

gorm.Model

Username string `json:"username" gorm:"column:username"`

// 添加外键关联

CreditCard []Card

}

// 2.2 定义Card表

type Card struct {

gorm.Model

Number string

// 这个就是User表关联的外键 名字是 结构体+主键

UserID uint

}

func main() {

// 1. 建立连接

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

fmt.Println(db)

// 3. 创建表结构

db.AutoMigrate(&User{}, &Card{})

// 4. 添加一对多

}

表结构

mysql> desc cards;

+------------+---------------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |

+------------+---------------------+------+-----+---------+----------------+

| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |

| created_at | datetime(3) | YES | | NULL | |

| updated_at | datetime(3) | YES | | NULL | |

| deleted_at | datetime(3) | YES | MUL | NULL | |

| number | longtext | YES | | NULL | |

| user_id | bigint(20) unsigned | YES | MUL | NULL | |

+------------+---------------------+------+-----+---------+----------------+

6 rows in set (0.00 sec)

mysql> desc users;

+------------+---------------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |

+------------+---------------------+------+-----+---------+----------------+

| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |

| created_at | datetime(3) | YES | | NULL | |

| updated_at | datetime(3) | YES | | NULL | |

| deleted_at | datetime(3) | YES | MUL | NULL | |

| username | longtext | YES | | NULL | |

+------------+---------------------+------+-----+---------+----------------+

5 rows in set (0.00 sec)

7.1.2 创建一对多数据

// 2. 创建一对多

// 2.1 定义User表

type User struct {

gorm.Model

Username string `json:"username" gorm:"column:username"`

// 添加外键关联

CreditCard []Card

}

// 2.2 定义Card表

type Card struct {

gorm.Model

Number string

// 这个就是User表关联的外键 名字是 结构体+主键

UserID uint

}

func main() {

// 1. 建立连接

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

fmt.Println(db)

// 3. 创建表结构

//db.AutoMigrate(&User{}, &Card{})

// 4. 添加一对多

user := User{

Username: "zhangsan",

CreditCard: []Card{

{Number: "00001"},

{Number: "00002"},

},

}

db.Create(&user)

fmt.Println(db)

}

此时数据已经被创建

mysql> select * from users;

+----+-------------------------+-------------------------+------------+----------+

| id | created_at | updated_at | deleted_at | username |

+----+-------------------------+-------------------------+------------+----------+

| 1 | 2022-12-07 10:55:11.397 | 2022-12-07 10:55:11.397 | NULL | zhangsan |

+----+-------------------------+-------------------------+------------+----------+

1 row in set (0.00 sec)

mysql> select * from cards;

+----+-------------------------+-------------------------+------------+--------+---------+

| id | created_at | updated_at | deleted_at | number | user_id |

+----+-------------------------+-------------------------+------------+--------+---------+

| 1 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL | 00001 | 1 |

| 2 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL | 00002 | 1 |

+----+-------------------------+-------------------------+------------+--------+---------+

2 rows in set (0.00 sec)

7.1.3 添加一对多数据

// 给zhangsan添加一个信用卡

// 查出zhangsan相关的记录

u := User{Username: "zhangsan"}

db.First(&u)

// Association 关联查找

db.Model(&u).Association("CreditCard").Append([]Card{

{Number: "00003"},

})

此时就在Card表中追加了一条00003的记录

mysql> select * from users;

+----+-------------------------+-------------------------+------------+----------+

| id | created_at | updated_at | deleted_at | username |

+----+-------------------------+-------------------------+------------+----------+

| 1 | 2022-12-07 10:55:11.397 | 2022-12-07 11:33:36.351 | NULL | zhangsan |

+----+-------------------------+-------------------------+------------+----------+

1 row in set (0.00 sec)

mysql> select * from cards;

+----+-------------------------+-------------------------+------------+--------+---------+

| id | created_at | updated_at | deleted_at | number | user_id |

+----+-------------------------+-------------------------+------------+--------+---------+

| 1 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL | 00001 | 1 |

| 2 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL | 00002 | 1 |

| 3 | 2022-12-07 11:33:36.352 | 2022-12-07 11:33:36.352 | NULL | 00003 | 1 |

+----+-------------------------+-------------------------+------------+--------+---------+

3 rows in set (0.00 sec)

7.2 一对多查找

7.2.1 Association

使用Association方法要把User查询好,然后根据User定义中指定的AssociationForeignKey去查找Card,性能较低

// 2. 创建一对多

// 2.1 定义User表

type User struct {

gorm.Model

Username string `json:"username" gorm:"column:username"`

// 添加外键关联

CreditCard []Card

}

// 2.2 定义Card表

type Card struct {

gorm.Model

Number string

// 这个就是User表关联的外键 名字是 结构体+主键

UserID uint

}

func main() {

// 1. 建立连接

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

// 3. 查询关联表数据

// 3.1 使用Association 方法把User查询好

// 3.2然后根据User中定义的AssociationForeignKey去查找Card

u := User{Username: "zhangsan"}

db.First(&u)

//fmt.Printf("%v", u.Username)

err := db.Model(&u).Association("CreditCard").Find(&u.CreditCard)

if err != nil {

fmt.Println("err", err)

}

fmt.Println(u)

// 转成json数据

strUser, err := json.Marshal(&u)

if err != nil {

fmt.Println("err", err)

}

fmt.Println(string(strUser))

结果

{"ID":1,"CreatedAt":"2022-12-07T10:55:11.397+08:00","UpdatedAt":"2022-12-07T11:33:36.351+08:00","DeletedAt":null,"username":"zhangs

an","CreditCard":[{"ID":1,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","DeletedAt":null,

"Number":"00001","UserID":1},{"ID":2,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","Delet

edAt":null,"Number":"00002","UserID":1},{"ID":3,"CreatedAt":"2022-12-07T11:33:36.352+08:00","UpdatedAt":"2022-12-07T11:33:36.352+08

:00","DeletedAt":null,"Number":"00003","UserID":1}]}

7.2.2 Preload预加载

// 2. 创建一对多

// 2.1 定义User表

type User struct {

gorm.Model

Username string `json:"username" gorm:"column:username"`

// 添加外键关联

CreditCard []Card

}

// 2.2 定义Card表

type Card struct {

gorm.Model

Number string

// 这个就是User表关联的外键 名字是 结构体+主键

UserID uint

}

func main() {

// 1. 建立连接

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

//fmt.Println(db)

// 3. 通过Preload进行一对多的查询

users := []User{}

db.Preload("CreditCard").Find(&users)

// 4. 通过FindOne查询

strUser, _ := json.Marshal(&users)

fmt.Println(string(strUser))

}

结果

[{"ID":1,"CreatedAt":"2022-12-07T10:55:11.397+08:00","UpdatedAt":"2022-12-07T11:33:36.351+08:00","DeletedAt":null,"username":"zhang

san","CreditCard":[{"ID":1,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","DeletedAt":null

,"Number":"00001","UserID":1},{"ID":2,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","Dele

tedAt":null,"Number":"00002","UserID":1},{"ID":3,"CreatedAt":"2022-12-07T11:33:36.352+08:00","UpdatedAt":"2022-12-07T11:33:36.352+0

8:00","DeletedAt":null,"Number":"00003","UserID":1}]}]

8. 多对多关系

双向的一对多,就是多对多 Many to Many 会在两个 model 中添加一张连接表。

8.1 many2many

type User struct {

gorm.Model

// Languages 是字段名

// gorm:"many2many指定了User表和Language是多对多关系 user_language是第三张表的表名

Languages []Language `gorm:"many2many:user_language"`

}

type Language struct {

gorm.Model

Name string

}

func main() {

// 1. 建立连接

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

fmt.Println(db)

// 3. 创建表结构

db.AutoMigrate(User{}, Language{})

}

执行后User和Language被创建,同时第三张表user_language也被创建

mysql> desc users;

+------------+---------------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |

+------------+---------------------+------+-----+---------+----------------+

| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |

| created_at | datetime(3) | YES | | NULL | |

| updated_at | datetime(3) | YES | | NULL | |

| deleted_at | datetime(3) | YES | MUL | NULL | |

+------------+---------------------+------+-----+---------+----------------+

4 rows in set (0.00 sec)

mysql> desc languages;

+------------+---------------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |

+------------+---------------------+------+-----+---------+----------------+

| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |

| created_at | datetime(3) | YES | | NULL | |

| updated_at | datetime(3) | YES | | NULL | |

| deleted_at | datetime(3) | YES | MUL | NULL | |

| name | longtext | YES | | NULL | |

+------------+---------------------+------+-----+---------+----------------+

5 rows in set (0.00 sec)

mysql> desc user_language;

+-------------+---------------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-------------+---------------------+------+-----+---------+-------+

| user_id | bigint(20) unsigned | NO | PRI | NULL | |

| language_id | bigint(20) unsigned | NO | PRI | NULL | |

+-------------+---------------------+------+-----+---------+-------+

2 rows in set (0.00 sec)

8.2 自定义第三张表

// 2. 定义多对多

// 一个用户可以多个地址,一个地址可以对应多个用户

type Persons struct {

Id string

Name string `gorm:"many2many:PersonAddress"`

}

type Addresses struct {

Id uint

Name string

}

// 3. 定义第三张表

type PersonAddresses struct {

PersonId string `gorm:"primaryKey"` // 对应表的主键

AddressId string `gorm:"primaryKey"` // 对应表的主键

CreateAt time.Time

}

func main() {

// 1. 建立连接

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

fmt.Println(db)

db.AutoMigrate(Persons{}, Addresses{}, PersonAddresses{})

}

创建后

mysql> show tables;

+-------------------+

| Tables_in_test_db |

+-------------------+

| addresses |

| languages |

| person_addresses |

| persons |

| user_language |

| users |

+-------------------+

6 rows in set (0.00 sec)

mysql> desc addresses;

+-------+---------------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |

+-------+---------------------+------+-----+---------+----------------+

| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |

| name | longtext | YES | | NULL | |

+-------+---------------------+------+-----+---------+----------------+

2 rows in set (0.00 sec)

mysql> desc persons;

+-------+--------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-------+--------------+------+-----+---------+-------+

| id | varchar(191) | NO | PRI | NULL | |

| name | longtext | YES | | NULL | |

+-------+--------------+------+-----+---------+-------+

2 rows in set (0.00 sec)

mysql> desc person_addresses;

+------------+--------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+------------+--------------+------+-----+---------+-------+

| person_id | varchar(191) | NO | PRI | NULL | |

| address_id | varchar(191) | NO | PRI | NULL | |

| create_at | datetime(3) | YES | | NULL | |

+------------+--------------+------+-----+---------+-------+

3 rows in set (0.00 sec)

9. 中间件

9.1 中间件

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数.这个钩子函数就是中间件,中间件适合处理一些公共的业务逻辑

9.2 全局中间件

// 定义一个全局中间件(所有路由都会使用)

// MiddleWare 函数名字,随便写.

// gin.HandlerFunc中间件必须要返回的方法

func MiddleWare() gin.HandlerFunc {

return func(c *gin.Context) {

fmt.Println("我是一个全局中间件")

}

}

func main() {

// 1. 实例化引擎

r := gin.Default()

// 5. 全局使用中间件

r.Use(MiddleWare())

// 2. 定义路由

r.GET("/", func(c *gin.Context) {

fmt.Println("访问了/")

c.JSON(200, gin.H{

"code": 200,

"msg": "Success",

})

})

fmt.Println("http://192.168.31.1:8000")

r.Run(":8000")

}

9.3 局部中间件

只对指定的路劲进行限制

// 定义一个全局中间件(所有路由都会使用)

// MiddleWare 函数名字,随便写.

// gin.HandlerFunc中间件必须要返回的方法

func MiddleWare() gin.HandlerFunc {

return func(c *gin.Context) {

fmt.Println("我是一个全局中间件")

}

}

func main() {

// 1. 实例化引擎

r := gin.Default()

// 5. 全局使用中间件

//r.Use(MiddleWare())

// 2. 定义路由

r.GET("/", func(c *gin.Context) {

fmt.Println("访问了/")

c.JSON(200, gin.H{

"code": 200,

"msg": "Success",

})

})

r.GET("/home", MiddleWare(), func(c *gin.Context) {

fmt.Println("访问了/home")

c.JSON(200, gin.H{

"code": 200,

"msg": "In Home",

})

})

fmt.Println("http://192.168.31.1:8000")

r.Run(":8000")

}

此时访问/不会再有加载中间件

但访问/home就会加载

访问了/

[GIN] 2022/12/07 - 15:26:20 | 200 | 0s | 192.168.31.1 | GET "/"

我是一个全局中间件

访问了/home

[GIN] 2022/12/07 - 15:26:26 | 200 | 608.7µs | 192.168.31.1 | GET "/home"

9.4 Next方法

先执行中间件.执行c.next(),那么就交给了视图函数执行视图函数执行完毕后,返回中间件继续往下执行

整个过程中MiddleWare只被调用了1次,处理完视图函数后继续执行后续操作.

// 定义一个全局中间件(所有路由都会使用)

// MiddleWare 函数名字,随便写.

// gin.HandlerFunc中间件必须要返回的方法

func MiddleWare() gin.HandlerFunc {

return func(c *gin.Context) {

fmt.Println("开始执行中间件")

c.Next()

fmt.Println("视图函数执行完成后再调用next()方法")

}

}

func main() {

// 1. 实例化引擎

r := gin.Default()

// 5. 全局使用中间件

r.Use(MiddleWare())

// 2. 定义路由

r.GET("/", func(c *gin.Context) {

fmt.Println("访问了/")

c.JSON(200, gin.H{

"code": 200,

"msg": "Success",

})

})

fmt.Println("http://192.168.31.1:8000")

r.Run(":8000")

}

结果

开始执行中间件

访问了/

视图函数执行完成后再调用next()方法

[GIN] 2022/12/07 - 15:31:53 | 200 | 0s | 192.168.31.1 | GET "/"

9.5 实现Token认证

/index页面无需登录即可访问 http://192.168.31.1:8000/index /home目录需要登录验证成功才能访问 http://192.168.31.1:8000/home

func Auth() gin.HandlerFunc {

return func(c *gin.Context) {

Token := c.Request.Header.Get("token")

fmt.Println("获取到的Token为: ", Token)

if Token != "twgdh" {

c.String(403, "身份验证失败,请先登录!")

c.Abort() // 终止当前请求,不再转发给路由,处理函数不再执行

return

}

c.Next()

}

}

func main() {

r := gin.Default()

// 无需登录就能访问

r.GET("/index", func(c *gin.Context) {

c.JSON(200, gin.H{"msg": "Index Page!无需登录就能访问"})

})

// 需要登录才能访问

r.GET("/home", Auth(), func(c *gin.Context) {

c.JSON(200, gin.H{"msg": "Home Page!您已登录成功"})

})

fmt.Println("http://192.168.31.1:3000/index")

fmt.Println("http://192.168.31.1:3000/home")

r.Run(":3000")

}

/index 无需token

/home 当token不等于twgdh时,提示登录失败

/home当获取到正确的token时,提示登录成功

10. Restful 风格

10.1 什么是RESTful

Restful是一种软件架构风格(REST是Representational State Transfer的简称,中文意思为: 表征状态转移)REST从资源角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识所有数据不过是通过网络获取还是操作(增删改查)的数据,都是资源,将一切数据视为资源就是REST区别于其他架构风格的最本质熟悉

10.2 web开发本质

对数据库的增删改查操作Restful风格就是把所有数据都当做资源,对表的操作就是对资源的操作在url通过资源名称来指定资源通过get/post/put/delete/patch 对资源进行操作

get 获取数据信息(一条或多条)post 添加数据put 修改数据delete 删除数据

10.2.1 URL路径

路径都使用名词或名词的复数,不要使用动词

获取所有信息 GET /products 获取1条信息 GET /products/4 新增信息 POST /products 更新一条信息 PUT /products/4

10.2.2 请求方式

访问一个URL地址,采用不同的求情方式,代表要执行不同的操作

请求方式说明GET获取一个或多个资源POST新增资源数据PUT修改资源数据DELETE删除资源数据

10.2.3 过滤信息

通过?进行数据过滤

?limit=10 指定返回记录的数量 ?offset=10 指定返回记录开始位置 ?page=2&pagesize=100 指定第几页开始,每页的记录数 ?sortby=name&order=asc 指定返回结果按照哪个熟悉排序,以及排序的顺序

10.2.4 响应状态码

状态码含义2xx都是请求成功的200请求成功,一般用于GET和POST请求201Created [POST/PUT/PATCH] 用户新建或修改数据成功202Accept [*] 表示一个请求已经进入后台排队(异步任务)204NO CONTENT [DELETE],用户删除数据成功3xx重定向301永久重定向302临时重定向4xx客户端错误400INVALID REQUEST [POST/PUT/PATCH] 用户发出的请求有错误.401Unauthorized [*] 授权错误 用户没有权限(令牌,用户名,密码错误)403Forbidden [*] 授权正确,但操作被禁止. 表示用户得到授权(与401相同),但访问是被禁止的.404NOT FOUND [*] 用户发出的请求记录不存在406Not Acceptale [GET] 用户请求的格式不可得(比如需要json,提交了xml)410Gone [GET] 用户请求的资源被永久删除了,切不会再得到.422Unprocesable entity [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误5xx服务端错误500INTERNAL SERVER ERROR [*] 服务器内部错误,无法完成请求501Not Implemented 服务器不支持请求的功能,无法完成请求