在上一篇文章深入gin框架内幕(一)中,主要介绍了Gin框架中是如何创建一个HTTP服务以及内部的核心结构和常用的一些结构体方法,并在最后以一个简单的示例来详细讲解Gin框架内部具体是如何运行的,但是在最后我们会发现使用了一个
Context
引用对象的一些方法来返回具体的HTTP响应数据,在本篇文章中,我们将继续学习和分析Gin框架内幕。
在开始分析之前,我们先简单回顾一下上一个章节中讲到的Gin框架中的几个核心的结构.
Gin框架中的几个核心结构
Gin框架中的几个重要的模型:
Engine
: 用来初始化一个gin
对象实例,在该对象实例中主要包含了一些框架的基础功能,比如日志,中间件设置,路由控制(组),以及handlercontext等相关方法.源码文件Router
: 用来定义各种路由规则和条件,并通过HTTP服务将具体的路由注册到一个由context实现的handler中- Context:
Context
是框架中非常重要的一点,它允许我们在中间件间共享变量,管理整个流程,验证请求的json以及提供一个json的响应体. 通常情况下我们的业务逻辑处理也是在整个Context引用对象中进行实现的. - Bind: 在Context中我们已经可以获取到请求的详细信息,比如HTTP请求头和请求体,但是我们需要根据不同的HTTP协议参数来获取相应的格式化
数据来处理底层的业务逻辑,就需要使用
Bind
相关的结构方法来解析context中的HTTP数据
1.Gin框架对HTTP响应数据的处理
我们在深入Gin框架内幕(一)中,以一个简单的Gin实例来具体讲解它内部是如何创建一个Http服务,并且注册一个路由来接收用户的请求,在示例程序中我们使用了Context
引用对象的String
方法来处理HTTP服务的数据响应,所以在整个Gin框架中紧跟Router
模型结构的就要属Context
结构了,该结构体主要用来处理整个HTTP请求的上下文数据,也是我们在开发HTTP服务中相对比较重要的一个结构体了。
|
|
我们可以看到,在使用Gin框架后,我们只需要很简单的代码,即可以快速运行一个返回Hello BGBiao.
的HTTP服务,而在ginObj.Any
方法中,我们传入了一个参数为Context
引用类型的匿名函数,并在该函数内部采用String(code,data)
方法来处理HTTP服务的响应数据(返回Hello BGBiao字符串),这个时候,你可能会想,我们在企业内部都是前后端分离,通常情况下后端仅会提供RESTful API
,并通过JSON
格式的数据和前端进行交互,那么Gin是如何处理其他非字符串类型的数据响应呢,这也是我们接下来要主要讲的Context
结构模型。
2.Gin框架中的Context结构体
注意:
在Gin框架中由Router
结构体来负责路由和方法(URL和HTTP方法)的绑定,内的Handler采用Context
结构体来处理具体的HTTP数据传输方式,比如HTTP头部,请求体参数,状态码以及响应体和其他的一些常见HTTP行为。
Context结构体
:
|
|
Context结构体常用的一些方法
基本方法
:
- Copy(): 返回当前正在使用的context的拷贝(context指针),当这个context必须在goroutine中用时,该方法比较有用
- HandlerName(): 返回当前主handler的名称(比如:handler为handleGetUsers(),该方法将返回"main.handleGetUsers")
- HandlerNames(): 返回所有注册的handler的名称
Handler()
: 返回当前的主handler(func (c *Context) Handler() HandlerFunc
)- FullPath(): 返回一个匹配路由的全路径(uri: “/user/:id”,c.FullPath() == “/user/:id” )
http常用方法
:
- ClientIP() string: 返回客户端ip(该方法会解析
X-Real-IP
,X-Forwarded-For
) - ContentType() string: 返回HTTP的Content-Type头
- IsWebsocket() bool: 返回是否为ws链接
流控相关的方法:
- Next(): 该方法仅被使用在middleware中,它会在被调用的handler链内部执行pending handler
- IsAborted(): 如果当前的context被终止了,该方法返回true
- Abort(): 该函数可以从正在被调用中保护pending handler. 该方法停止后不会停止当前正在执行的handler. 比如我们有一个鉴权的中间件来验证请求是否有权限,如果认证失败了(用户信息异常等),此时调用Abort()来确保后面的handler不再被调用
- AbortWithStatus(code int): 同上,在会写入状态码。context.AbortWithStatus(401)即可表示上述的鉴权失败
- AbortWithStatusJSON(code int, jsonObj interface{}): 同上,会再加响应数据.该方法会停止整个handler链,再写入状态码和json的响应体,同时也会设置Content-Type=“application/json”
- AbortWithError(code int, err error) *Error: 同上返回错误信息
错误管理
:
- Error(err error) *Error: 返回一些错误对象
元数据管理
:
- Set(key string, value interface{}): 给当前这个context设置一个新的键值对
- Get(key string) (value interface{}, exists bool): 返回指定的key的值,以及是否存在
- MustGet(key string) interface{}: 返回指定key的值,不存在则panic
- GetString(key string) (s string): 以string类型返回指定的key
- GetBool(key string) (b bool): 返回分配给该key的值(bool类型)
- GetInt(key string) (i int):
- GetStringSlice(key string) (ss []string): 返回key的slice类型
- GetStringMap(key string) (sm map[string]interface{}): 返回interface{}类型的map结构
- GetStringMapString(key string) (sms map[string]string): 返回string类型的map结构
- GetStringMapStringSlice(key string) (smss map[string][]string): 同理
输入数据
:
- Param(key string) string: 返回URL的参数值(uri_patten: “/user/:id”,url: “/user/john”,c.Param(“id”) = “john”)
- Query(key string) string: 返回url中的查询参数值(url: “/path?id=1234&name=Manu&value=",c.Query(“id”)为1234,c.Query(“name”)为Manu,c.Query(“value”)为空)
- DefaultQuery(key, defaultValue string) string: 返回url中的查询参数的默认值(同上,但是c.Query(“value”)就没有值,该方法可以设置默认值)
- GetQuery(key string) (string, bool): 同Query()方法,并且会返回状态,如果对应的key不存在,返回(”",false)
- QueryArray(key string) []string: 返回指定key的对应的array(slice的长度取决于给定key的参数的数量)
- GetQueryArray(key string) ([]string, bool): 同上,会返回状态
- QueryMap(key string) map[string]string: 返回指定key对应map类型
- GetQueryMap(key string) (map[string]string, bool): 同上,并且会返回状态
- PostForm(key string) string: 该方法返回一个从POST 请求的urlencode表单或者multipart表单数据,不存在时返回空字符串
- DefaultPostForm(key, defaultValue string) string: 同上,key不存在时返回默认值
- GetPostForm(key string) (string, bool): 同PostForm()方法,并且会返回状态
- PostFormArray(key string) []string: 该方法返回指定key的字符串类型的slice
- GetPostFormArray(key string) ([]string, bool): 同上,并返回状态
- PostFormMap(key string) map[string]string: 返回指定key的map类型
- GetPostFormMap(key string) (map[string]string, bool): 同上,并返回状态
- FormFile(name string) (*multipart.FileHeader, error): 返回指定key的第一个文件(用作文件上传)
- MultipartForm() (*multipart.Form, error): 该方法解析multipart表单,包含file文件上传
- SaveUploadedFile(file *multipart.FileHeader, dst string) error: 该方法用来上传指定的文件头到目标路径(dst)
Bind家族相关方法
:
- Bind(obj interface{}) error: 自动解析
Content-Type
并绑定到指定的binding引擎 - BindJSON(obj interface{}) error: 同上,binding引擎为
binding.JSON
- BindXML(obj interface{}) error:
- BindQuery(obj interface{}) error:
- BindYAML(obj interface{}) error:
- BindHeader(obj interface{}) error:
- BindUri(obj interface{}) error: 使用
binding.Uri
来绑定传递的结构体指针 - MustBindWith(obj interface{}, b binding.Binding) error: 使用指定的binding引擎来绑定传递的结构体指针(当有任何错误时,终止请求并返回400)
ShouldBind家族相关方法
:
- ShouldBind(obj interface{}) error: 同上述的Bind()方法,但是该方法在json结构无效时不会返回400
- ShouldBindJSON(obj interface{}) error:
- ShouldBindXML(obj interface{}) error:
- ShouldBindQuery(obj interface{}) error:
- ShouldBindYAML(obj interface{}) error:
- ShouldBindHeader(obj interface{}) error:
- ShouldBindUri(obj interface{}) error:
- ShouldBindWith(obj interface{}, b binding.Binding) error: 等同于MustBindWith()方法
- ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error): 和ShouldBindWith()方法相似,但是他会存储请求体到context中,当下次调用时可以重用(因为该方法是在binding之前读取body,因此在你只使用一次时,为了更好的性能还是使用ShouldBindWith会比较好)
HTTP响应相关的方法
:
- Status(code int): 设置http的响应码
- Header(key, value string): 是
c.Writer.Header().Set(key, value)
的简单实现,在响应体重写入一个header,如果value为空,则相当于调用了c.Writer.Header().Del(key)
- GetHeader(key string) string: 返回请求体重的header
- GetRawData() ([]byte, error): 返回流式数据
- SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool): 该方法将设置一个Set-Cookie到ResponseWriter的头中(注意:name必须是一个合法可用的名称,无效的coookie可能会被丢弃)
- Cookie(name string) (string, error): 返回名称为name的cookie
- Render(code int, r render.Render): 该方法写入响应头并调用render.Render去渲染数据
- HTML(code int, name string, obj interface{}): 该方法使用指定文件模板名称去渲染http模板(同时会更新状态码并设置Content-Type as “text/html”.)
- IndentedJSON(code int, obj interface{}): 该方法会序列化对象obj为一个pretty JSON 数据到响应体中,同时设置Content-Type as “application/json”(pretty JSON需要消耗cpu和带宽,强烈建议生产使用
Context.JSON()
) - SecureJSON(code int, obj interface{}): 同上,会序列化成
Secure Json
- JSONP(code int, obj interface{}):
- JSON(code int, obj interface{}): 序列化为JSON,并写Content-Type:“application/json"头
- AsciiJSON(code int, obj interface{}):
- PureJSON(code int, obj interface{}):
- XML(code int, obj interface{}): 序列化成
xml
格式,并写Content-Type:“application/xml” - YAML(code int, obj interface{}): 序列化成
yaml
- ProtoBuf(code int, obj interface{}): 序列化成
probuf
- String(code int, format string, values …interface{}): 将制定的string写入响应体
- Redirect(code int, location string): 重定向
- Data(code int, contentType string, data []byte): 写一些数据到响应体重,并更新响应码
- DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string): 写一些制定模板的数据到响应体中,并更新状态码
- File(filepath string): 以一种高效方式将制定文件写入响应体数据中
- FileAttachment(filepath, filename string): 同上,但是在客户端文件会被直接下载下来
- SSEvent(name string, message interface{}): 写Server-Sent Event到响应数据中
- Stream(step func(w io.Writer) bool) bool: 发送一个流式的响应数据并返回状态
3.Gin实例示例
3.1返回json格式的数据
为了解决我们在开头提到的问题,我们将使用context引用对象的JSON家族方法来处理该需求
|
|
当然上面我们仅以JSON格式来示例,类似的方式我们可以使用XML
,YAML
,ProtoBuf
等方法来输出指定格式化后的数据。
3.2其他常用的基本方法
注意:
在其他基本方法中我们仍然使用上述示例代码中的主逻辑,主要用来测试基本的方法.
|
|
3.3用户数据输入
当然到这里后,你可能还会有新的疑问,就是通常情况下,我们开发后端接口会提供一些具体的参数,通过一些具体数据提交来实现具体的业务逻辑处理,这些参数通常会分为如下三类:
- 使用HTTP GET方法获取到的url中的一些查询参数来执行更具体的业务逻辑(比如我们查询数据的指定条数之类的)
- 使用HTTP POST GET等其他方式以form表单方式提交的数据来验证和处理用户数据
- 在URL中获取一些可变参数(比如通常我们的url会定义为”/api/uid/:id"来表示用户id相关的接口,这个时候通常需要获取到url中的id字段)
以上的基本需求,几乎都可以在Context结构体的输入数据
中找到响应的方法.
|
|