type
status
date
slug
summary
tags
category
icon
password
Gin
一、Gin 介绍
Gin 是一个 Go (Golang) 编写的轻量级 http web 框架,运行速度非常快,如果你是性能和高效的追求者,我们推荐你使用 Gin 框架。
Gin 最擅长的就是 Api 接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐您使用 Gin。
当某个接口的性能遭到较大挑战的时候,这个还是可以考虑使用 Gin 重写接口。
Gin 也是一个流行的 golang Web 框架,Github Strat 量已经超过了 50k。
Gin 的官网:https://gin-gonic.com/zh-cn/
Gin Github 地址:https://github.com/gin-gonic/gin
二、Gin 环境搭建
要安装 Gin 软件包,需要先安装 Go 并设置 Go 工作区。
1.下载并安装 gin:
2.将 gin 引入到代码中:
3.(可选)如果使用诸如 http.StatusOK 之类的常量,则需要引入 net/http 包:
4、新建 Main.go 配置路由
5、运行你的项目
6、要改变默认启动的端口
如果 go get 失败请参考:
三、golang 程序的热加载
所谓热加载就是当我们对代码进行修改时,程序能够自动重新加载并执行,这在我们开发中是非常便利的,可以快速进行代码测试,省去了每次手动重新编译
beego 中我们可以使用官方给我们提供的 bee 工具来热加载项目,但是 gin 中并没有官方提供的热加载工具,这个时候我们要实现热加载就可以借助第三方的工具。
四、Gin 框架中的路由
4.1、路由概述
路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路由的时候建议参考 RESTful API 指南。
在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作:
4.2、简单的路由配置
简单的路由配置(可以通过 postman 测试)
当用 GET 请求访问一个网址的时候,做什么事情:
当用 POST 访问一个网址的时候,做什么事情:
当用 PUT 访问一个网址的时候,执行的操作:
当用 DELETE 访问一个网址的时候,执行的操作:
路由里面获取 Get 传值
域名/news?aid=20
动态路由
域名/user/20
4.3、 c.String() c.JSON() c.JSONP() c.XML() c.HTML()
返回一个字符串
返回一个 JSON 数据
JSOPN
返回 XML 数据
渲染模板
五、Gin HTML 模板渲染
5.1、全部模板放在一个目录里面的配置方法
1、我们首先在项目根目录新建 templates 文件夹,然后在文件夹中新建 index.html
2、Gin 框架中使用 c.HTML 可以渲染模板,渲染模板前需要使用 LoadHTMLGlob()或者LoadHTMLFiles()方法加载模板。
5.2、模板放在不同目录里面的配置方法
Gin 框架中如果不同目录下面有同名模板的话我们需要使用下面方法加载模板
- *注意:**定义模板的时候需要通过 define 定义名称
templates/admin/index.html
<!-- 相当于给模板定义一个名字 define end 成对出现-->
templates/default/index.html
<!-- 相当于给模板定义一个名字 define end 成对出现-->
业务逻辑
- *注意:如果模板在多级目录里面的话需要这样配置 r.LoadHTMLGlob("templates/\\/\\/*") / 表示目录
5.3、 gin 模板基本语法
1、 {{.}} 输出数据
模板语法都包含在{{和}}中间,其中{{.}}中的点表示当前对象。
当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如:
业务逻辑
模板
<!-- 相当于给模板定义一个名字 define end 成对出现-->
2、注释
{{/* a comment */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。
3、变量
我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法如下:
4、移除空格
有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{{-语法去除模板内容左侧的所有空白符号, 使用-}}去除模板内容右侧的所有空白符号。
例如:
注意:-要紧挨{{和}},同时与模板值之间需要使用空格分隔。
5、比较函数
布尔函数会将任何类型的零值视为假,其余视为真。
下面是定义为函数的二元比较运算的集合:
eq 如果 arg1 == arg2 则返回真
ne 如果 arg1 != arg2 则返回真
lt 如果 arg1 < arg2 则返回真
le 如果 arg1 <= arg2 则返回真
gt 如果 arg1 > arg2 则返回真
ge 如果 arg1 >= arg2 则返回真
6、条件判断
Go 模板语法中的条件判断有以下几种:
6、range
Go 的模板语法中使用 range 关键字进行遍历,有以下两种写法,其中 pipeline 的值必须是数组、切片、字典或者通道。
如果 pipeline 的值其长度为 0,不会有任何输出
如果 pipeline 的值其长度为 0,则会执行 T0。
7、With
以前要输出数据:
现在要输出数据:
简单理解:相当于 var .=.user
8、预定义函数 (了解)
执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用 Funcs 方法添加函数到模板里。
预定义的全局函数如下:
and
- 函数返回它的第一个 empty 参数或者最后一个参数;就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
- 返回第一个非 empty 参数或者最后一个参数;亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
- 返回它的单个参数的布尔值的否定
len
- 返回它的参数的整数类型长度
index
- 执行结果为第一个参数以剩下的参数为索引/键指向的值;如"index x 1 2 3"返回 x[1]\[2][3]的值;每个被索引的主体必须是数组、切片或者字典。
print
- 即 fmt.Sprint
printf
- 即 fmt.Sprintf
println
- 即 fmt.Sprintln
html
- 返回与其参数的文本表示形式等效的转义 HTML。这个函数在 html/template 中不可用。
urlquery
- 以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。这个函数在 html/template 中不可用。
js
- 返回与其参数的文本表示形式等效的转义 JavaScript。
call
- 执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
- 如"call .X.Y 1 2"等价于 go 语言里的 dot.X.Y(1, 2);
- 其中 Y 是函数类型的字段或者字典的值,或者其他类似情况;
- call 的第一个参数的执行结果必须是函数类型的值(和预定义函数如 print 明显不同);
- 该函数类型值必须有 1 到 2 个返回值,如果有 2 个则后一个必须是 error 接口类型;
- 如果有 2 个返回值的方法返回的 error 非 nil,模板执行会中断并返回给调用模板执行者该错误;
9、自定义模板函数
模板里面的用法
5.4、嵌套 template
1、新建 templates/deafult/page_header.html
2、外部引入
注意:
1、引入的名字为 page_header.html 中定义的名字
2、引入的时候注意最后的点(.)
六、静态文件服务
当我们渲染的 HTML 文件中引用了静态文件时,我们需要配置静态 web 服务
r.Static("/static", "./static") 前面的/static 表示路由 后面的./static 表示路径
七、路由详解
路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
前面章节我们给大家介绍了路由基础以及路由配置,这里我们详细给大家讲讲路由传值、路由返回值
7.1、GET POST 以及获取 Get Post 传值
7.1.1、Get 请求传值
GET /user?uid=20&page=1
7.1.2、动态路由传值
域名/user/20
7.1.3、Post 请求传值 获取 form 表单数据
定义一个 add_user.html 的页面
通过 c.PostForm 接收表单传过来的数据
7.1.4、获取 GET POST 传递的数据绑定到结构体
为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的 Content-Type识别请求数据类型并利用反射机制自动提取请求中 QueryString、form 表单、JSON、XML 等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取 JSON、form 表单和 QueryString 类型的数据,并把值绑定到指定的结构体对象。
Get 传值绑定到结构体
/?username=zhangsan&password=123456
返回数据
{"user":"zhangsan","password":"123456"}
Post 传值绑定到结构体
返回数据
{"user":"zhangsan","password":"123456"}
7.1.5、获取 Post Xml 数据
在 API 的开发中,我们经常会用到 JSON 或 XML 来作为数据交互的格式,这个时候我们可以在 gin 中使用 c.GetRawData()获取数据。
7.2、简单的路由组
7.3、Gin 路由文件 分组
7.3.1、新建 routes 文件夹,routes 文件下面新建 adminRoutes.go、apiRoutes.go、defaultRoutes.go
1、新建 adminRoutes.go
2、新建 apiRoutes.go
3、新建 defaultRoutes.go
7.3.2 、配置 main.go
访问 /api/user /admin/user 测试
八、Gin 中自定义控制器
8.1、控制器分组
当我们的项目比较大的时候有必要对我们的控制器进行分组
新建 controller/admin/NewsController.go
新建 controller/admin/UserController.go
配置对应的路由 --adminRoutes.go
其他路由的配置方法类似
8.2、控制器的继承
1、新建 controller/admin/BaseController.go
2、NewsController 继承 BaseController
继承后就可以调用控制器里面的公共方法了
九、Gin 中间件
Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作
9.1、路由中间件
9.1.1、初识中间件
Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函数,最后一个 func 回调函数前面触发的方法都可以称为中间件。
9.1.2、ctx.Next()调用该请求的剩余处理程序
中间件里面加上 ctx.Next()可以让我们在路由匹配完成后执行一些操作。
比如我们统计一个请求的执行时间。
9.1.3、一个路由配置多个中间件的执行顺序
控制台内容:
initMiddlewareOne--1-执行中中间件
initMiddlewareTwo--1-执行中中间件
执行路由里面的程序
initMiddlewareTwo--2-执行中中间件
initMiddlewareOne--2-执行中中间件
9.1.4、 c.Abort()--(了解)
Abort 是终止的意思, c.Abort() 表示终止调用该请求的剩余处理程序
initMiddlewareOne--1-执行中间件
initMiddlewareTwo--1-执行中间件
initMiddlewareTwo--2-执行中间件
initMiddlewareOne--2-执行中间件
9.2、全局中间件
9.3、在路由分组中配置中间件
1、为路由组注册中间件有以下两种写法
写法 1:
写法 2:
2、分组路由 AdminRoutes.go 中配置中间件
9.4、中间件和对应控制器之间共享数据
设置值
获取值
中间件设置值
控制器获取值
9.5、中间件注意事项
gin 默认中间件
gin.Default()默认使用了 Logger 和 Recovery 中间件,其中:
- Logger 中间件将日志写入 gin.DefaultWriter,即使配置了 GIN_MODE=release。
- Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500 响应码。
如果不想使用上面两个默认的中间件,可以使用 gin.New()新建一个没有任何默认中间件的路由。
gin 中间件中使用 goroutine
当在中间件或 handler 中启动新的 goroutine 时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())
十、Gin 中自定义 Model
10.1、关于 Model
如果我们的应用非常简单的话,我们可以在 Controller 里面处理常见的业务逻辑。但是如果我们有一个功能想在多个控制器、或者多个模板里面复用的话,那么我们就可以把公共的功能单独抽取出来作为一个模块(Model)。 Model 是逐步抽象的过程,一般我们会在 Model里面封装一些公共的方法让不同 Controller 使用,也可以在 Model 中实现和数据库打交道
10.2、Model 里面封装公共的方法
1、新建 models/ tools.go
10.3、控制器中调用 Model
10.4、调用 Model 注册全局模板函数
models/tools.go
//时间戳间戳转换成日期
main.go
//注册全局模板函数 注意顺序,注册模板函数需要在加载模板上面
控制器
模板
10.5、Golang Md5 加密
打开 golang 包对应的网站:https://pkg.go.dev/,搜索 md5
方法一:
方法二:
十一、Gin 文件上传
注意:需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"
11.1、单文件上传
官方示例:
项目中实现文件上传:
1、定义模板 需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"
2、定义业务逻辑
11.2、多文件上传--不同名字的多个文件
1、定义模板 需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"
2、定义业务逻辑
11.3、多文件上传--相同名字的多个文件
1、定义模板 需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"
2、定义业务逻辑
11.4、文件上传 按照日期存储
1、定义模板 需要在上传文件的 form 表单上面需要加入 enctype="multipart/form-data"
2、定义业务逻辑
3、models/tools.go
十二、Gin 中的 Cookie
12.1、Cookie 介绍
- HTTP 是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何关系的。如果我们要实现多个页面之间共享数据的话我们就可以使用 Cookie 或者 Session 实现
- cookie 是存储于访问者计算机的浏览器中。可以让我们用同一个浏览器访问同一个域名的时候共享数据。
12.2、Cookie 能实现的功能
1、保持用户登录状态
2、保存用户浏览的历史记录
3、猜你喜欢,智能推荐
4、电商网站的加入购物车
12.3、设置和获取 Cookie
设置 Cookie
第一个参数 key
第二个参数 value
第三个参数 过期时间.如果只想设置 Cookie 的保存路径而不想设置存活时间,可以在第三个参数中传递 nil
第四个参数 cookie 的路径
第五个参数 cookie 的路径 Domain 作用域 本地调试配置成 localhost , 正式上线配置成域名
第六个参数是 secure ,当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效
第七个参数 httpOnly,是微软对 COOKIE 做的扩展。如果在 COOKIE 中设置了“httpOnly”属性,则通过程序(JS 脚本、applet 等)将无法读取到 COOKIE 信息,防止 XSS 攻击产生
获取 Cookie
完整 demo
12.4 、多个二级域名共享 cookie
1、分别把 a.itying.com 和 b.itying.com 解析到我们的服务器
2、我们想的是用户在 a.itying.com 中设置 Cookie 信息后在 b.itying.com 中获取刚才设置的cookie,也就是实现多个二级域名共享 cookie
这时候的话我们就可以这样设置 cookie
十三、Gin 中的 Session
13.1、Session 简单介绍
session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 session保存在服务器上。
13.2、Session 的工作流程
当客户端浏览器第一次访问服务器并发送请求时,服务器端会创建一个 session 对象,生成一个类似于 key,value 的键值对,然后将 value 保存到服务器 将 key(cookie)返回到浏览器(客户)端。浏览器下次访问时会携带 key(cookie),找到对应的 session(value)。
13.3、Gin 中使用 Session
Gin 官方没有给我们提供 Session 相关的文档,这个时候我们可以使用第三方的 Session 中间件来实现
gin-contrib/sessions 中间件支持的存储引擎:
- cookie
- memstore
- redis
- memcached
- mongodb
13.4、基于 Cookie 存储 Session
1、安装 session 包
2、基本的 session 用法
13.5、基于 Redis 存储 Session
如果我们想将 session 数据保存到 redis 中,只要将 session 的存储引擎改成 redis 即可。
使用 redis 作为存储引擎的例子:
首先安装 redis 存储引擎的包
例子:
十四、Gin 中使用 GORM 操作 mysql 数据库
14.1、GORM 简单介绍
GORM 是 Golang 的一个 orm 框架。简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用 ORM框架可以让我们更方便的操作数据库。
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
Gorm 特性
- 全功能 ORM
- 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持 Preload、Joins 的预加载
- 事务,嵌套事务,Save Point,Rollback To Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
- SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- Auto Migration
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
- 每个特性都经过了测试的重重考验
- 开发者友好
14.2、Gin 中使用 GORM
1、安装
如果使用 go mod 管理项目的话可以忽略此步骤
2、Gin 中使用 Gorm 连接数据库
在 models 下面新建 core.go ,建立数据库链接
3、定义操作数据库的模型
Gorm 官方给我们提供了详细的:
虽然在 gorm 中可以指定字段的类型以及自动生成数据表,但是在实际的项目开发中,我们是先设计数据库表,然后去实现编码的。
在实际项目中定义数据库模型注意以下几点:
1、结构体的名称必须首字母大写 ,并和数据库表名称对应。例如:表名称为 user 结构体名称定义成 User,表名称为 article_cate 结构体名称定义成 ArticleCate
2、结构体中的字段名称首字母必须大写,并和数据库表中的字段一一对应。例如:下面结构体中的 Id 和数据库中的 id 对应,Username 和数据库中的 username 对应,Age 和数据库中的 age 对应,Email 和数据库中的 email 对应,AddTime 和数据库中的 add_time 字段对应
3、默认情况表名是结构体名称的复数形式。如果我们的结构体名称定义成 User,表示这个模型默认操作的是 users 表。
4、我们可以使用结构体中的自定义方法 TableName 改变结构体的默认表名称,如下:
表示把 User 结构体默认操作的表改为 user 表
定义 user 模型:
关于更多模型定义的方法参考:https://gorm.io/zh_CN/docs/conventions.html
gorm.Model
GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt
// gorm.Model 的定义
14.3、Gin GORM CURD
找到要操作数据库表的控制器,然后引入 models 模块
1、增加
增加成功后会返回刚才增加的记录
2、查找
查找全部
指定条件查找
3、修改
更多修改的方法参考:https://gorm.io/zh_CN/docs/update.html
4、删除
更多删除的方法参考:https://gorm.io/zh_CN/docs/delete.html
5、批量删除
更多删除的方法参考:https://gorm.io/zh_CN/docs/delete.html
14.4、Gin GORM 查询语句详解
1、Where
=
<
\>
<=
\>=
!=
IS NOT NULL
IS NULL
BETWEEN AND
NOT BETWEEN AND
IN
OR
AND
NOT
LIKE
2、Or 条件
3、选择字段查询
4、排序 Limit 、Offset
跳过 2 条查询 2 条
5、获取总数
6、Distinct
从模型中选择不相同的值
7、Scan
8、Join (先了解 后面课程会讲关联查询)
14.4、Gin GORM 查看执行的 sql
十五、原生 SQL 和 SQL 生成器
更多使用方法参考:
1、使用原生 sql 删除 user 表中的一条数据
2、使用原生 sql 修改 user 表中的一条数据
3、查询 uid=2 的数据
4、查询 User 表中所有的数据
5、统计 user 表的数量
十六、Gin 中使用 GORM 实现表关联查询
16.1、一对一
如上图所示,一个文章只有一个分类,article 和 article_cate 之间是 1 对 1 的关系。
文章表中的 cate_id 保存着文章分类的 id。
如果我们想查询文章的时候同时获取文章分类,就涉及到 1 对 1 的关联查询。
foreignkey 指定当前表的外键、references 指定关联表中和外键关联的字段
Article
ArticleCate
1、查询所有文章以及文章对应的分类信息:
- *注意:**Preload("ArticleCate")里面的 ArticleCate 为 Article struct 中定义的属性 ArticleCate
返回 JSON 数据:
2、查询所有文章以及文章对应的分类信息 指定条件:
返回数据:
16.2、一对多
一对多在实际项目中用的非常多
比如一个点餐系统:有菜品分类、有菜品。 菜品分类和菜品之间就是一对多的关系订单表和订单商品表:订单表和订单商品表之间也是一对多的关系
如上图所示,一个分类下面有很多个文章,article_cate 和 article 之间是 1 对多的关系。
文章表中的 cate_id 保存着文章分类的 id。
如果我们想查询文章分类的时候获取分类下面的文章,这个时候就涉及到一对多的关联查询。
ArticleCate
//ArticleCate 的结构体
Article
1、查找所有分类以及分类下面的文章信息
返回数据:
2、查找所有分类以及分类下面的文章信息 指定条件
3、更多 1 对多的查询方法
4、如果我们的程序中有如下需求
1、查询文章获取文章分类信息
2、查询文章分类获取文章信息
这个时候可以这样定义 models
16.3、多对多
1、定义学生 课程 学生课程表 model
如果想根据课程获取选学本门课程的学生,这个时候就在 Lesson 里面关联 Student
Lesson
Student
LessonStudent
2、获取学生信息 以及课程信息
3、查询学生信息的时候获取学生的选课信息
4、查询张三选修了哪些课程
5、课程被哪些学生选修了
6、计算机网络被那些学生选修了
7、查询数据指定条件
8、关联查询指定子集的筛选条件
张三被开除了 查询课程被哪些学生选修的时候要去掉张三
用法:
9、自定义预加载 SQL
查看课程被哪些学生选修 要求:学生 id 倒序输出
- *注意:**需要引入 gorm.io/gorm 这个包
十七、GORM 中使用事务
事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全执行,要么全不执行
17.1、禁用默认事务
为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。
GORM 默认会将单个的 create, update, delete 操作封装在事务内进行处理,以确保数据的完整性。
如果你想把多个 create, update, delete 操作作为一个原子操作,Transaction 就是用来完成这个的。
17.2、事务
1、事务执行流程
要在事务中执行一系列操作,通常您可以参照下面的流程来执行。
2、事务(手动控制)
3、张三给李四转账
十八、Gin 中使用 go-ini 加载.ini 配置文件
18.1、go-ini 介绍
go-ini 官方介绍,go-ini 是地表 最强大、最方便 和 最流行 的 Go 语言 INI 文件操作库。
Github 地址:https://github.com/go-ini/ini
官方文档 :https://ini.unknwon.io/
18.2、go-ini 使用
1、新建 conf/app.ini
现在,我们编辑 my.ini 文件并输入以下内容
很好,接下来我们需要编写 main.go 文件来操作刚才创建的配置文件。
18.3、从.ini 中读取 mysql 配置
- 作者:lveMonsi
- 链接:https://blog.lvems.top/article/231011
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。