gin-web开发

1 gin介绍

Gin 是一个用 Golang编写的 高性能的web 框架, 由于http路由的优化,速度提高了近 40 倍。 Gin的特点就是封装优雅、API友好。

Gin的一些特性:

  • 快速 基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。

  • 支持中间件 传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。

  • Crash 处理 Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!

  • JSON 验证 Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。

  • 路由组 更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。

  • 错误管理 Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。

  • 内置渲染 Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。

  • 可扩展性 新建一个中间件非常简单。

安装十分简单:

go get -u github.com/gin-gonic/gin

2 创建一个简单的服务

package main
// 导入gin包
import "github.com/gin-gonic/gin"

// 入口函数
func main() {
    // 初始化一个http服务对象
    // 默认使用Logger and Recovery中间件
	r := gin.Default()
        
    // 设置一个get请求的路由,url为/ping, 处理函数(或者叫控制器函数)是一个闭包函数。
	r.GET("/ping", func(c *gin.Context) {
    	// 通过请求上下文对象Context, 直接往客户端返回一个json
    	// type H map[string]any
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

    // 不写具体address默认在0.0.0.0:8080启动
	r.Run(":8080") // 监听并在 0.0.0.0:8080 上启动服务
}

3 设置图标

4 创建路由

4.1 路由匹配规则

Gin 框架的路由匹配规则基于 HTTP 方法​ 和 URL 路径,支持静态路由、参数路由、通配符等多种模式。

路由类型
语法示例
匹配的示例路径
说明

静态路由

/user

/user

精确匹配,只匹配路径完全相同的请求。

路径参数

/user/:id

/user/123

匹配一个路径段,:id匹配任意非空字符串(不包括斜杠)。

通配符路由

/file/*filepath

/file/abc/def.jpg

匹配从/file/开始的所有路径,包括斜杠。*filepath必须放在路径最后。

Gin 按以下顺序匹配路由(静态路径优先):

  1. 完全匹配的静态路径(如 /user

  2. 路径参数(如 /user/:id

  3. 通配符(如 /file/*filepath

注意事项

  1. 路径斜杠

    • Gin 默认严格匹配斜杠,/user/user/被视为不同路径。

    • 可通过 RedirectFixedPath选项自动重定向修正斜杠。

  2. 参数冲突

    • 避免重叠的路由定义(如同时定义 /user/:id/user/name),可能意外匹配。

  3. 通配符位置

    • 通配符只能放在路径末尾,如 /*path有效,但 /user/*path/invalid无效。

5 参数渲染

在后端中可以给前端传递各种类型的参数

6 获取请求参数

7 数据绑定

Gin提供了两类绑定方法:

  • Must bind

    • Bind、BindJSON、BindXML、BindQuery、BindYAML

    • 这些方法属于MustBindWith的具体调用。如果发生绑定错误,则请求终止,并触发 c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind)

    • 响应码被设置为400并且 Content-Type被设置为 text/plain;charset-utf-8

    • 只能把响应码设置为400-422之间

  • Should bind

    • ShouldBind、ShouldBindJSON、ShouldBindXML、ShouldBindQuery、ShouldBindYAML

    • 这些方法都属于 ShouldBindWith调用。如果发生绑定错误,Gin会返回错误并由开发者处理错误和请求。

  • 使用 Bind 方法时,Gin 会尝试根据 Content-Type 推断如何绑定。 如果你明确知道要绑定什么,可以使用 MustBindWithShouldBindWith

  • 你也可以指定必须绑定的字段。 如果一个字段的 tag 加上了 binding:"required",但绑定时是空值, Gin 会报错。

标签类型
绑定位置
常见场景
示例请求

**form:"name"**​

URL Query 或 Form Data

HTML 表单提交、GET 请求参数

?name=张三&age=20

**uri:"name"**​

URL 路径参数

RESTful API 资源标识

/users/123

**json:"name"**​

请求体 JSON

API 请求、前端传参

{"name":"张三","age":20}

7.1 数据验证

Gin使用 go-playground/validator/v10arrow-up-right 进行验证。 查看标签用法的全部文档arrow-up-right. 不满足校验会报错:Error:Field validation for 'Desc' failed on the 'required' tag

如果有多个验证字段,按顺序进行验证,如果前面验证不通过不会对后面的进行验证:

    • :忽略字段

  • required:不为空校验,binding:"required"

  • regexp:正则表达式校验

  • min:最小长度,binding:"min=1"

  • max:最大长度

  • |:或

  • structonly:如果有嵌套可以决定只验证结构体上的

  • Exists

  • omitempty:省略空,如果为空,则不会继续校验该字段其他的规则,只有不为空才会继续验证其他的

  • dive:嵌套验证

  • len:长度

  • eq/ne/gte/ge/lt/lte:gt、gte、lt、lte等都可以用于时间的比较,后面可以不跟值,表示大于当前utc时间

  • eqfield:等于其他字段的值

  • nefield:不等于其他字段的值

  • eqcsfield:类似eqfield,它会验证相对于顶层结构提供的字段

  • nqcsfield

  • gtfield:大于其他字段的值

  • gtefield

  • gtcsfield

  • gtecsfield

  • alpha:字符串仅包含字母字符

  • alphanum:仅包含字母数字字符

  • numeric:字符串包含基本数字值。不包含指数等

  • hexadecimal:字符串包含有效的十六进程

  • hexcolor:字符串值包含有效的十六进程颜色,包括#

  • rgb:包含有效的rgb颜色

  • rgba

  • email:包含有效的电子邮件

  • url:包含有效的网址,必须包含http://等

  • uri:包含有效的uri。将接受golang请求uri接受的任何uri

  • contains:包含子字符串

  • excludes:排除

  • uuid

  • ip

7.2 自定义验证器

  1. 安装包

  1. 定义验证器

  1. 注册验证器

  1. 使用验证器

7.3 beego验证器

  1. 下载包

  1. 常用验证器

  2. 使用

  1. 自定义错误信息

    1. #TODO:如何使用beego自定义验证器

8 文件上传

9 响应数据类型

10 路由组

11 中间件

  • 什么是中间件

    • 开发者自定义的钩子函数

  • 中间件的作用

    • 中间件适合处理一些公共的业务逻辑,比如登陆认证、权限校验、数据分页、记录日志、耗时统计等

    • 需要对某一类的函数进行通用的前置或后置处理

  • 中间件的回调要先于用户定义的路由处理函数

  • Use是追加中间件,会按照顺序调用中间件

  • 常用中间件

11.1 自定义中间件

11.2 Next和Abort

  • Next

    • 在定义的众多中间件会形成一条中间件链,而通过Next函数来对后面的中间件进行执行

    • 当遇到c.Netx()函数时,它取出所有的没有被执行过的注册函数都执行一边,然后再回到本函数中

    • Next函数是在请求前执行,而Next函数后是在请求后执行

      • c.Next() 之前的代码会在请求处理前(Pre-Request)​ 执行。

      • ​**c.Next() 之后的代码会在请求处理后(Post-Request)​** 执行(即响应已生成,但尚未发送给客户端)。

    • 可以用在token校验,把用户id存起来给功能性函数使用

  • Abort

    • 终止调用整个链条,但只是不会调用此函数所有中间件的后面中间件,本中间件和之前的中间件会调用完毕

    • 比如token认证没有通过,不能直接使用return返回,而是使用Abort来终止

11.3 利用Next计算请求时间

11.4 中间件作用域

  • 全局中间件:直接在创建出来的路由引擎中使用

  • 路由组中间件:在创建出来的路由组直接使用,该路由组下的所有路由都将使用该中间件

  • 局部中间件:直接在路由上使用

11.5 gin.BasicAuth()中间件

11.6 WrapH和WrapF

12 模板语法

统一使用{{和}}作为左右标签,在该左右标签中的内容会被解析为Go代码逻辑(如变量、条件、循环等),在左右标签之外的内容原样输出。

12.1 上下文

  • .:访问当前位置的上下文

  • $:引用当前模板根级的上下文

  • $.:引用模板中的根级上下文

12.2 去除空格

  • {{- :去除前空格

  • -}}:去除后空格

12.3 支持go语言的符号

  1. 字符串:{{"zhaoli"}}

  2. 原始字符串:{{`yes`}}

  3. 字节类型:{{'a'}} ->97字符编码号

  4. nil类型:{{print nil}}

    • {{nil}}只有nil会报错:nil is not a command

12.4 定义变量

  1. 定义:

  1. 使用

注意:只能在当前模板使用

12.5 pipline

可以是上下文的变量输出,也可以是函数通过管道传递的返回值

  • {{.Name}}是上下文的变量输出,是个pipline

  • {{"h"|len}}是函数通过管道传递的返回值,是个pipline

12.6 流程控制-if

  1. if...else...

  1. if嵌套

12.7 循环range

12.8 with

  • 作用:用于重定向pipline

12.9 template

  • 作用:导入其他的模板

  • 导入的模板文件也要使用define包含

  • 如果想在引用的模板中获取动态数据,必须使用.访问当前位置的上下文

  • 引入的作用位置跟 template使用的位置有关

12.10 模板函数

  • print

    • print:fmt.Sprint

    • printf:fmt.Sprintf

    • println:fmt.Sprintln

格  式
描  述

%v

按值的本来值输出

%+v

在 %v 基础上,对结构体字段名和值进行展开

%#v

输出 Go 语言语法格式的值

%T

输出 Go 语言语法格式的类型和值

%%

输出 % 本体

%b

整型以二进制方式显示

%o

整型以八进制方式显示

%d

整型以十进制方式显示

%x

整型以十六进制方式显示

%X

整型以十六进制、字母大写方式显示

%U

Unicode 字符

%f

浮点数

%p

指针,十六进制方式显示

  • 括号:设置优先级

  • and:只要有一个为空,则整体为空,如果都不为空,则返回最后一个

  • or:只要有有一个不为空,则返回第一个不为空的,否则返回空

  • call:是一个动态调用其他函数或方法的工具,主要用于在模板中灵活执行传入的函数或方法。

  • index:读取指定类型对于下标的值

    • 支持map/array/slice/string

  • len:返回指定类型的长度

  • not:返回输入参数的否定值,布尔类型

  • urlquery: 有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用该符号的编码

  • 判断符,返回布尔值

    • eq:等于,可以用于字符串判断

      • 支持多个参数,只要有一个与第一个值相等就返回true

    • ne:不等于

    • lt:大于

    • le:大于等于

    • gt:小于

    • ge:小于等于

  • Format:实现时间的格式化,返回字符串。也可以在后端转换在前端使用。

    • {{.time_data.Format "2006/01/02 15:04:05"}}

  • html:转义文本中的html标签

  • js:返回用JavaScript的escape处理后的文本

    • escape函数可以对字符串进行编码,这样就可以在所有的计算机上读取该字符串,可以使用unescape解码

  • slice:对string/slice/array进行切片

12.10.1 自定义模板函数

  1. 定义函数

  1. 注册函数:注册函数要在控制器加载静态文件之前

  1. 使用函数

13 日志

13.1 基于gin的日志中间件

  • 使用日志文件

13.2 logrus

#TODO:logrus

  • 下载包

  • 定义logrus配置文件

  • 设置logrus

  • logrus使用

  • 什么是session

    • Session是在无状态的http协议下,服务端记录用户状态时用于标识具体用户的机制

    • 它是在服务端保存的用来跟踪用户状态的数据结构,可以保存在文件、数据库或者集群中

    • 在浏览器关闭后这次的Session就消失了,下次打开就不再拥有这个Session。其实并不是Session消失了,而是Session ID变了。

  • 什么是Cookie

    • Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息

    • 每次HTTP请求时,客户端都会发送相应的Cookie信息到服务端。它的过期时间可以任意设置,如果不主动清除它,很长一段时间都可以保留

  • session和cookie

    • Cookie在客户端,Session在服务器端

    • Cookie安全性一般,他人可以通过分析存放在本地的Cookie并进行Cookie欺骗。在安全性第一的前提下,选择Session更优。重要的交互信息比如权限等放在Session中,一般的信息记录放在Cookie

    • 单个Cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个Cookie

    • Session可以放在文件、数据库或内存中

    • 用户验证这种场合一般会用到Session。因此维持一个会话的核心就是客户端的唯一标识,即Session ID

    • Session的运行依赖Session ID,而Session ID是存在Cookie中的,也就是说,如果浏览器禁用了Cookie,Session也会失效(但是可以通过其他方式实现,比如在url中传递SessionID)

#TODO :怎么发送session并认证

  • 下载包

  • 基于Cookie存储引擎使用

  • 基于redis存储引擎

最后更新于