gin是目前golang的主要web框架之一,之所以选择这个框架是因为其拥有高效的路由性能,并且有人长期维护,目前github上的star数已经破3W。

[安装]

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

基础使用:

package main

import (
"github.com/gin-gonic/gin"
"net/http"
) func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context){
c.JSON(http.StatusOK, gin.H{
"messag": "pong",
})
}) r.Run(":8099")
}

运行服务: go run main.go

其中默认使用中间件: logger 和recovery

路由:

请求方法包括:get, post, patch, delete and options。此外还有any,即任何请求方法都会监听到。

func main() {
router := gin.Default() router.GET("/someGet", handle)
router.POST("/somePost", handle)
router.PUT("/somePut", handle)
router.DELETE("/someDelete", handle)
router.PATCH("/somePatch", handle)
router.HEAD("/someHead", handle)
router.OPTIONS("/someOptions", handle) router.ANY("/any", handle) router.Run()
} func handle(context *gin.Context) {
context.String(http.StatusOK, "hello world")
}

分组路由可以通过router.Group:

func main() {
router := gin.Default() v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
} v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
} router.Run(":8080")
}

请求:

Path参数:

func main() {
router := gin.Default() // 匹配/user/john
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
}) // 匹配/user/john/和/user/john/send
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
}) router.Run(":8080")
}

query参数:

func main() {
router := gin.Default() // welcome?firstname=Jane&lastname=Doe
router.GET("/user", func(c *gin.Context) {
firstname := c.DefaultQuery("name", "kim") // 获取query中的name,没有的话就为kim
lastname := c.Query("age") // 获取query中的age c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}

multipart/urlencoded form参数:

func main() {
router := gin.Default() router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("age")
nick := c.DefaultPostForm("name", "kim") c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
router.Run(":8080")
}

模型绑定与参数校验:

我们已经见识了x-www-form-urlencoded类型的参数处理,现在越来越多的应用习惯使用JSON来通信,也就是无论返回的response还是提交的request,其content-type类型都是application/json的格式。而对于一些旧的web表单页还是x-www-form-urlencoded的形式,这就需要我们的服务器能改hold住这多种content-type的参数了。
由于go是静态语言,需要先实现定义数据模型,这就需要用到gin的model bind功能了。

gin使用go-playground/validator.v8验证参数,查看完整文档

需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置json:"fieldname" 。
此外,Gin还提供了两套绑定方法:

  • Must bind

    • Methods - BindBindJSONBindXMLBindQueryBindYAML
    • Behavior - 这些方法底层使用 MustBindWith,如果存在绑定错误,请求将被以下指令中止 c.AbortWithError(400, err).SetType(ErrorTypeBind),响应状态代码会被设置为400,请求头Content-Type被设置为text/plain; charset=utf-8。注意,如果你试图在此之后设置响应代码,将会发出一个警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422,如果你希望更好地控制行为,请使用ShouldBind相关的方法
  • Should bind
    • Methods - ShouldBindShouldBindJSONShouldBindXMLShouldBindQueryShouldBindYAML
    • Behavior - 这些方法底层使用 ShouldBindWith,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。

当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用MustBindWith或者BindingWith
你还可以给字段指定特定规则的修饰符,如果一个字段用binding:"required"修饰,并且在绑定时该字段的值为空,那么将返回一个错误。

package main

import (
"net/http" "github.com/gin-gonic/gin"
) type Person struct {
Name string `json:"name" binding:"required"` // json格式从name取值,并且该值为必须的
Age int `json:"age" binding:"required,gt=20"` // json格式从age取值,并且该值为必须的,且必须大于20
} func main() { router := gin.Default() router.POST("/test", func(context *gin.Context) {
var person Person

// var person = Person{
// Name: "default", // 设置参数默认值, 若不存在该值的时候
// }
// 这里我确定传过来的一定是JSON所以用ShouldBindJSON,否则可以用ShouldBind
if err := context.ShouldBindJSON(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
}) router.Run(":3000")
}

自定义验证器:

package main

import (
"net/http"
"reflect"
"time" "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v8"
) type Booking struct {
// 这里的验证方法为bookabledate
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
// gtfield=CheckIn表示大于的字段为CheckIn
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
} func bookableDate(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
// 这里有两个知识点,映射和断言
// 在这里,field是一个reflect.Type的接口类型变量,通过Interface方法获得field接口类型变量的真实类型,可以理解为reflect.Value的逆操作
// 在这里,断言就是将一个接口类型的变量转化为time.Time,前提是后者必须实现了前者的接口
// 综上,这里就是将field进行了类型转换
if date, ok := field.Interface().(time.Time); ok {
today := time.Now()
if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
return false
}
}
return true
} func main() {
route := gin.Default() // 注册自定义验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
} route.GET("/bookable", getBookable)
route.Run(":8085")
} func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

中间件:

中间件可以在我们接受到一个http请求时,在handle之前或者handle之后做一些处理。通常,在handle之前,我们可以通过中间件很方便地进行校验,如果再handle之后,我们可以对response进行一些调整。

// 创建一个不包含中间件的路由器
gin.New()
// 使用自定义中间件或者gin提供的中间件
gin.use(gin.Logger()) 可代替:
gin.Default()

其实gin默认使用了Logger和Recovery两个中间件,然后也是在内部调用了New:

// gin.go

func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery()) // 使用了Logger和Recovery两个中间件
return engine
}

我们对这两个中间件做一个简单的了解:

  • Logger中间件可以让我们做打印的一些自定义配置
  • Recovery中间件可以让我们从崩溃中恢复
func main() {

    logfile, _ := os.Create("./logs/gin.log")

    // 这里将log输出到指定文件
// 注意这个配置一定要在gin.Default()之前
gin.DefaultWriter = io.MultiWriter(logfile, os.Stdout) router := gin.Default() // 这里分别使用两个中间件
router.Use(gin.Logger())
router.Use(gin.Recovery()) router.POST("/test", func(context *gin.Context) {
var person Person
if err := context.ShouldBind(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
}) router.Run(":3000")
}

自定义中间件:

// recovery.go

这里只要返回一个HandlerFunc类型即可
func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
} // gin.go HandlerFunc就是一个参数为*context的函数
type HandlerFunc func(*Context)

我们来写一个IP鉴权的中间件,假设我们的需求是只有白名单中的ip才可以访问服务器,那么我们可以这么实现:

ipauth.go

// ipauth.go

func Auth() gin.HandlerFunc {
return func(context *gin.Context) {
// 定义ip白名单
whiteList := []string{
"127.0.0.1",
} ip := context.ClientIP() flag := false for _, host := range whiteList {
if ip == host {
flag = true
break
}
} if !flag {
context.String(http.StatusNetworkAuthenticationRequired, "your ip is not trusted: %s", ip)
context.Abort()
} }
}

main.go

// main.go

func main() {

    router := gin.New()

    router.Use(ipauth.Auth())

    router.GET("/test", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"success": true,
})
}) router.Run(":3000")
}

Group中使用中间件

此外,我们的中间件可以不全局使用,而只针对部分的group:

func main() {

    router := gin.Default()

  // 定义了group
authorized := router.Group("/auth", ipauth.Auth()) // 对上面这个group进行路由绑定
authorized.GET("/write", handle) router.GET("/read", handle) router.Run(":3000")
} func handle(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"success": true,
})
}

单个路由使用中间件:

func main() {
router := gin.Default() // 注册一个路由,使用了 middleware1,middleware2 两个中间件
router.GET("/someGet", middleware1, middleware2, handler) // 默认绑定 :8080
router.Run()
} func handler(c *gin.Context) {
log.Println("exec handler")
} func middleware1(c *gin.Context) {
log.Println("exec middleware1") //你可以写一些逻辑代码 // 执行该中间件之后的逻辑
c.Next()
} func middleware2(c *gin.Context) {
log.Println("arrive at middleware2")
// 执行该中间件之前,先跳到流程的下一个方法
c.Next()
// 流程中的其他逻辑已经执行完了
log.Println("exec middleware2") //你可以写一些逻辑代码
}

可以看出,中间件的写法和路由的 Handler 几乎是一样的,只是多调用c.Next()。正是有个c.Next(),我们可以在中间件中控制调用逻辑的变化,看下面的 middleware2 代码。在 middleware2中,执行到 c.Next()时,Gin 会直接跳到流程的下一个方法中,等到这个方法执行完后,才会回来接着执行 middleware2 剩下的代码。

所以请求上面注册的路由 url /someGet ,请求先到达middleware1,然后到达 middleware2,但此时 middleware2调用了 c.Next(),所以 middleware2的代码并没有执行,而是跳到了 handler ,等 handler执行完成后,跳回到 middleware2,执行 middleware2剩下的代码。

在中间件中使用goroutines

在中间件或处理程序中启动新的goroutine时,你不应该使用其中的原始上下文,你必须使用只读副本

func main() {
r := gin.Default() r.GET("/long_async", func(c *gin.Context) {
// 创建要在goroutine中使用的副本
cCp := c.Copy()
go func() {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second) // 这里使用你创建的副本
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
}) r.Run(":3000")
}

中间件中的程序终端Abort

func Auth() {
return func(c *gin.Context){
fmt.Println("before...") c.Abort() // 终端当前请求,不会执行到controller层中具体逻辑, 其他中间件仍会执行,和该中间件中之后的逻辑
return // 当前中间件中剩余逻辑不会被执行 fmt.Println("after...")
}
}

  

[日志设置]

使用:

    //f, err := os.Create("gin.log")  // 重启之后会清空该文件
f, err := os.OpenFile("gin.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
gin.DefaultWriter = io.MultiWriter(f, os.Stdout) //fErr, _ := os.Create("gin-err.log") // 重启之后会清空该文件
// 内部发生panic错误的时候,并且没有进行异常捕获的情况下,会将其输入到该文件, 若存在异常中间件的话,则自定义进行处理即可,并不会输出到该文件中了
fErr, err := os.OpenFile("gin-err.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
gin.DefaultErrorWriter = io.MultiWriter(fErr, os.Stderr) // 设置运行模式
gin.SetMode(cfg.App.RunMode)
// HTTP Server
handler := gin.New()
...

[运行模式]

系统支持三种运行模式: debug,test,release

  debug: 会输出所有信息, 包含启动服务的时候的提示信息以及路由信息等

test:不会输出服务启动信息与路由信息, 之后输出路由访问信息

release: 显示信息与test模式的一致

判断运行模式方式:

func IsRelease() bool {
return gin.Mode() == gin.ReleaseMode

Golang框架之gin的更多相关文章

  1. 01 . Go框架之Gin框架从入门到熟悉(路由和上传文件)

    Gin框架简介 Gin是使用Go/Golang语言实现的HTTP Web框架, 接口简洁, 性能极高,截止1.4.0版本,包含测试代码,仅14K, 其中测试代码9K, 也就是说测试源码仅5k左右, 具 ...

  2. 01 . Go之从零实现Web框架(类似Gin)

    设计一个框架 大部分时候,我们需要实现一个 Web 应用,第一反应是应该使用哪个框架.不同的框架设计理念和提供的功能有很大的差别.比如 Python 语言的 django和flask,前者大而全,后者 ...

  3. Web框架之Gin

    Gin是一个用Go语言编写的web框架.它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter,速度提高了近40倍. 如果你是性能和高效的追求者, 你会爱上Gin. ...

  4. Web框架之Gin介绍及使用

    Gin是一个用Go语言编写的web框架.它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter,速度提高了近40倍. 如果你是性能和高效的追求者, 你会爱上Gin. ...

  5. 1.1 安装gin框架&使用gin编写简单服务端

    01.安装gin框架 1)go环境配制 a)配制环境变量 GOPATH修改为go的工作文件夹路径 D:\Golang\goproject GOROOT修改为go的安装路径 D:\Golang\go1. ...

  6. golang 框架 之 CHI

    1,特性 golang 的 http框架已经有很多,究其原因:go 的net/http 已经做了大部分工作,封装一个框架的工作量,没有那么的大. 不过呢,林子大了鸟也就多了,部分框架的代码质量实在不敢 ...

  7. Golang框架beego和bee的开发使用

    Golang语言简洁.明细,语法级支持协程.通道.err,非常诱惑人.平时也看了看Golang的语法,正苦于没有需求,我想把beego的源码搬过来看看. 首先,第一步:beego环境的搭建 在我之前看 ...

  8. Go语言基础之20--web编程框架之Gin框架

    一.Gin框架介绍 1.1 简介 A. 基于httprouter开发的web框架. http://github.com/julienschmidt/httprouter B. 提供Martini风格的 ...

  9. Golang框架beego电影网爬虫小试牛刀

    学习了一段时间golang,又参考课程学习了beego开发网站爬虫,项目的目录结构是: 采集的目标是豆瓣网电影,入口地址是:https://movie.douban.com/subject/19008 ...

  10. Golang框架Beego在Windows环境下小试牛刀

    Beego官网beego官网 : https://beego.me/github : https://github.com/beego Beego安装前提: ①Go 1.1+ 以确保所有功能的正常使用 ...

随机推荐

  1. 【YashanDB知识库】IMP跨网络导入慢问题

    问题现象 问题单:imp性能慢-通过异机导入性能下降太多-镜像环境可重现 现象: 同样一份数据290M, 在同一个机器本地导入,耗时2分钟多,本机用ip连接导入耗时4分钟多, 跨机器导入,耗时17分钟 ...

  2. Unity中利用遗传算法训练MLP

    Unity中利用遗传算法训练MLP 梯度下降法训练神经网络通常需要我们给定训练的输入-输出数据,而用遗传算法会便捷很多,它不需要我们给定好数据,只需要随机化多个权重进行N次"繁衍进化&quo ...

  3. 贝壳找房携手 Flutter,为三亿家庭提供更好的居住服务 | Flutter 开发者故事

    贝壳找房是科技驱动的新居住服务平台,致力于在二手房.新房.租房以及装修等居住领域为三亿家庭提供全方位的品质居住服务.如此庞大的用户群体,自然也有着十分多样和复杂的使用场景和需求.以往使用原生开发模式时 ...

  4. AtCoder Regular Contest 182(A B C)

    原来第二题比第一题简单吗 A.Chmax Rush! \(\texttt{Diff 1110}\) 给定三个序列 \(S,P,V\),其中 \(S\) 的长度为 \(N\),\(P,V\) 的长度为 ...

  5. 关于pytorch中@和*的用处

    1.@是用来对tensor进行矩阵相乘的: import torch d = 2 n=50 X = torch.randn(n,d) true_w = torch.tensor([[-1.0],[2. ...

  6. OOOPS:零样本实现360度开放全景分割,已开源 | ECCV'24

    全景图像捕捉360°的视场(FoV),包含了对场景理解至关重要的全向空间信息.然而,获取足够的训练用密集标注全景图不仅成本高昂,而且在封闭词汇设置下训练模型时也受到应用限制.为了解决这个问题,论文定义 ...

  7. 21 如何写出一篇高质量的sci水文

    博客配套视频链接: https://www.bilibili.com/video/BV1fW4y1W7dS/ b 站直接看 模型确定, 结果正在跑(或已结束), 目标期刊已定,一般可以定顶刊 从目标期 ...

  8. (系列七).net8 Aop切面编程

    说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...

  9. Squid设置用户名密码

    在ubutnu上设置squid代理认证 为了在Ubuntu上设置Squid代理身份验证,您需要对Squid配置文件进行以下一些调整: 生成Squid代理身份验证密码 htpasswd是两种可用于生成代 ...

  10. 将NC栅格表示时间维度的数据提取出来的方法

      本文介绍基于Python语言,逐一读取大量.nc格式的多时相栅格文件,导出其中所具有的全部时间信息的方法.   .nc是NetCDF(Network Common Data Form)文件的扩展名 ...