gin简介

Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,已经发布了1.0版本。具有快速灵活,容错方便等特点。其实对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错。框架更像是一些常用函数或者工具的集合。借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。


Gin 包括以下几个主要的部分:


设计精巧的路由/中间件系统;

简单好用的核心上下文 Context;

附赠工具集(JSON/XML 响应, 数据绑定与校验等).

安装

终端输入:

使用的时候要导入包:

helloworld

新建一个go文件(helloworld.go):

使用Gin实现Hello world非常简单,创建一个router(路由),然后执行Run方法即可。


代码结构:


1、router:=gin.Default():这是默认的服务器。使用gin的Default方法创建一个路由Handler;

2、然后通过Http方法绑定路由规则和路由函数。不同于net/http库的路由函数,gin进行了封装,把request和response都封装到了gin.Context的上下文环境中。

3、最后启动路由的Run方法监听端口。还可以用http.ListenAndServe(":8080", router),或者自定义Http服务器配置。

路由

服务器

默认服务器

如果不传端口号,默认8080

http.ListenAndServe()
http.ListenAndServe()
http.ListenAndServe()

因此我们也可以自定义HTTP服务器的配置:

当然Server还有很多其他配置,这里不一一列举

路由

基本路由

httprouter

路由参数

gin的路由来自httprouter库。因此httprouter具有的功能,gin也具有,不过gin不支持路由正则表达式。

api参数

api参数通过Context的Param方法来获取


冒号:加上一个参数名组成路由参数。可以使用c.Params的方法读取其值。当然这个值是字串string。诸如/user/hanru,和/user/hello都可以匹配,而/user/和/user/hanru/不会被匹配。


URL参数

URL 参数通过 DefaultQuery 或 Query 方法获取。


对于参数的处理,经常会出现参数不存在的情况,对于是否提供默认值,gin也考虑了,并且给出了一个优雅的方案,使用c.DefaultQuery方法读取参数,其中当参数不存在的时候,提供一个默认值。使用Query方法读取正常参数,当参数不存在的时候,返回空字串。

表单参数

常见的格式就有四种。例如application/json,application/x-www-form-urlencoded, application/xml和multipart/form-data。后面一个主要用于图片上传。json格式的很好理解,urlencode其实也不难,无非就是把query string的内容,放到了body体里,同样也需要urlencode。默认情况下,c.PostFROM解析的是x-www-form-urlencoded或from-data的参数。


表单参数通过 PostForm 方法获取:


文件上传

上传单个文件

multipart/form-data

使用c.Request.FormFile解析客户端文件name属性。如果不传文件,则会抛错,因此需要处理这个错误。此处我们略写了错误处理。一种是直接用c.SaveUploadedFile()保存文件。另一种方式是使用os的操作,把文件数据复制到硬盘上。


上传多个文件

所谓多个文件,无非就是多一次遍历文件,然后一次copy数据存储即可。

Model

数据解析绑定

模型绑定可以将请求体绑定给一个类型。目前Gin支持JSON、XML、YAML和标准表单值的绑定。简单来说,,就是根据Body数据类型,将数据赋值到指定的结构体变量中 (类似于序列化和反序列化) 。

Gin提供了两套绑定方法:

Must bind

方法:Bind,BindJSON,BindXML,BindQuery,BindYAML

行为:这些方法使用MustBindWith。如果存在绑定错误,则用c终止请求,使用c.AbortWithError (400) .SetType (ErrorTypeBind)即可。将响应状态代码设置为400,Content-Type header设置为text/plain;charset = utf - 8。请注意,如果在此之后设置响应代码,将会受到警告:[GIN-debug][WARNING] Headers were already written. Wanted to override status code 400 with 422将导致已经编写了警告[GIN-debug][warning]标头。如果想更好地控制行为,可以考虑使用ShouldBind等效方法。

Should bind

方法:ShouldBind,ShouldBindJSON,ShouldBindXML,ShouldBindQuery,ShouldBindYAML

行为:这些方法使用ShouldBindWith。如果存在绑定错误,则返回错误,开发人员有责任适当地处理请求和错误

注意,使用绑定方法时,Gin 会根据请求头中 Content-Type 来自动判断需要解析的类型。如果你明确绑定的类型,你可以不用自动推断,而用 BindWith 方法。 你也可以指定某字段是必需的。如果一个字段被 binding:"required" 修饰而值却是空的,请求会失败并返回错误。

JSON绑定

JSON的绑定,其实就是将request中的Body中的数据按照JSON格式进行解析,解析后存储到结构体对象中。

前面我们使用c.String返回响应,顾名思义则返回string类型。content-type是plain或者text。调用c.JSON则返回json数据。其中gin.H封装了生成json的方式,是一个强大的工具。使用golang可以像动态语言一样写字面量的json,对于嵌套json的实现,嵌套gin.H即可。


Form表单

其实本质是将c中的request中的body数据解析到form中。首先我们先看一下绑定普通表单的例子:

方式二

Uri绑定

响应

既然请求可以使用不同的content-type,响应也如此。通常响应会有html,text,plain,json和xml等。 gin提供了很优雅的渲染方法。

JSON/XML/YAML渲染

HTML模板渲染

gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据。

LoadHTMLGlob()LoadHTMLFiles()

创建一个目录:templates,然后在该目录下创建一个模板文件:

templates/index.tmpl

运行项目,打开浏览器输入地址:http://127.0.0.1:8080/index

不同文件夹下模板名字可以相同,此时需要 LoadHTMLGlob() 加载两层模板路径。

gin也可以使用自定义的模板引擎,如下

文件响应

静态文件服务

可以向客户端展示本地的一些文件信息,例如显示某路径下地文件。服务端代码是:

重定向

同步异步

goroutine 机制可以方便地实现异步处理。当在中间件或处理程序中启动新的Goroutines时,你不应该在原始上下文使用它,你必须使用只读的副本。

中间件middleware

golang的net/http设计的一大特点就是特别容易构建中间件。gin也提供了类似的中间件。需要注意的是中间件只对注册过的路由函数起作用。对于分组路由,嵌套使用中间件,可以限定中间件的作用范围。中间件分为全局中间件,单个路由中间件和群组中间件。


我们之前说过, Context 是 Gin 的核心, 它的构造如下:

handlers[]HandlerFunc
HandlerFuncHandlerFunc

全局中间件

定义一个中间件函数:

该函数很简单,只会给c上下文添加一个属性,并赋值。后面的路由处理器,可以根据被中间件装饰后提取其值。需要注意,虽然名为全局中间件,只要注册中间件的过程之前设置的路由,将不会受注册的中间件所影响。只有注册了中间件以下代码的路由函数规则,才会被中间件装饰。

/middlerwarerouter.Use(MiddleWare())

使用花括号包含被装饰的路由函数只是一个代码规范,即使没有被包含在内的路由函数,只要使用router进行路由,都等于被装饰了。想要区分权限范围,可以使用组返回的对象注册中间件。

Next()方法

怎么解决一个请求和一个响应经过我们的中间件呢?神奇的语句出现了, 没错就是 c.Next(),所有中间件都有 Request 和 Response 的分水岭, 就是这个 c.Next(),否则没有办法传递中间件。


如果将c.Next()放在fmt.Println("after middleware,", status)后面,那么fmt.Println("after middleware,", status)和fmt.Println("request:",request)执行的顺序就调换了。c.Next()执行的位置。c.Next()的核心代码如下:

它其实是执行了后面所有的handlers。


一个请求过来, Gin 会主动调用 c.Next() 一次。因为 handlers 是 slice ,所以后来者中间件会追加到尾部。这样就形成了形如 m1(m2(f())) 的调用链。正如上面数字① ② 标注的一样, 我们会依次执行如下的调用:

MustGet

单个路由中间件

gin也提供了针对指定的路由函数进行注册。

把上述代码写在router.Use(Middleware())之前,同样也能看见/before被装饰了中间件。


中间件实践

中间件最大的作用,莫过于用于一些记录log,错误handler,还有就是对部分接口的鉴权。下面就实现一个简易的鉴权中间件。


简单认证BasicAuth

关于使用gin.BasicAuth() middleware, 可以直接使用一个router group进行处理, 本质和上面的一样。


定义私有数据:

然后使用 gin.BasicAuth 中间件,设置授权用户

最后定义路由:

然后启动项目,打开浏览器输入以下网址:http://127.0.0.1:8080/admin/secrets


总结

全局中间件router.Use(gin.Logger()) router.Use(gin.Recovery())

单路由的中间件,可以加任意多个router.GET("/benchmark", MyMiddelware(), benchEndpoint)

群组路由的中间件 authorized := router.Group("/", MyMiddelware()) 或者这样用:authorized := router.Group("/") authorized.Use(MyMiddelware()) { authorized.POST("/login", loginEndpoint) }

————————————————