Go Gin源码学习(三)

栏目: Go · 发布时间: 5年前

内容简介:在第一篇中看到了Gin提供了很多的获取和解析参数的方法:其中从url中获取 从get参数中获取 从post拿数据相信我们都可以想象的到,基本就是从request中的url或者body中获取数据然后返回 但是其中的数据绑定我自己开始是很疑惑的,到底是怎么实现的。疑惑的是如果object中我客户端少输入了参数 或者多输入的参数会是怎么样。举个例子:如果客户端输入的是{"user":"TAO","age":10} 没有输入password 得到的对象是什么会不会报错 如果客户端输入的是{"user":"TAO",

学习目标

在第一篇中看到了Gin提供了很多的获取和解析参数的方法:

// **** 输入数据
//从URL中拿值,比如 /user/:id => /user/john
Param(key string) string    

//从GET参数中拿值,比如 /path?id=john
GetQueryArray(key string) ([]string, bool)  
GetQuery(key string)(string, bool)
Query(key string) string
DefaultQuery(key, defaultValue string) string
GetQueryArray(key string) ([]string, bool)
QueryArray(key string) []string

//从POST中拿数据
GetPostFormArray(key string) ([]string, bool)
PostFormArray(key string) []string 
GetPostForm(key string) (string, bool)
PostForm(key string) string
DefaultPostForm(key, defaultValue string) string

// 文件
FormFile(name string) (*multipart.FileHeader, error)
MultipartForm() (*multipart.Form, error)
SaveUploadedFile(file *multipart.FileHeader, dst string) error

// 数据绑定
Bind(obj interface{}) error //根据Content-Type绑定数据
BindJSON(obj interface{}) error
BindQuery(obj interface{}) error

//--- Should ok, else return error
ShouldBindJSON(obj interface{}) error 
ShouldBind(obj interface{}) error
ShouldBindJSON(obj interface{}) error
ShouldBindQuery(obj interface{}) error

其中从url中获取 从get参数中获取 从post拿数据相信我们都可以想象的到,基本就是从request中的url或者body中获取数据然后返回 但是其中的数据绑定我自己开始是很疑惑的,到底是怎么实现的。疑惑的是如果object中我客户端少输入了参数 或者多输入的参数会是怎么样。举个例子:

type Login struct {
	User     string `json:"user"`
	Password string `json:"password"`
	Age      int
}

//伪代码
l := Login{}
context.Bind(&l)

如果客户端输入的是{"user":"TAO","age":10} 没有输入password 得到的对象是什么会不会报错 如果客户端输入的是{"user":"TAO","age":10,"class":"class"}多输入了一个class的参数 结果是什么 如果客户端输入的是{} 这边会不会报错,会得到什么结果 这些都是需要看源码

源码

先看gin/binding/binding.go

//Binding 接口 定义了一个返回名字的函数
//还有一个Bind 函数接口 这个是关键函数 //从request中获取数据解析
//所有类型的处理对象都继承这个接口来实现多态
type Binding interface {
	Name() string
	Bind(*http.Request, interface{}) error
}

type BindingBody interface {
	Binding
	BindBody([]byte, interface{}) error
}

//在包中定义了8个对象 可以供给context直接使用
var (
	JSON          = jsonBinding{}
	XML           = xmlBinding{}
	Form          = formBinding{}
	Query         = queryBinding{}
	FormPost      = formPostBinding{}
	FormMultipart = formMultipartBinding{}
	ProtoBuf      = protobufBinding{}
	MsgPack       = msgpackBinding{}
)

接下来是继承Binding接口的对象,这样的对象有很多我们就举一个例子JSON,JSON是我们使用的最多的一个方式。 gin/binding/json.go

//type 是Gin处理json对象使用的类 继承Binding
type jsonBinding struct{}

func (jsonBinding) Name() string {
	return "json"
}
//调用decodeJSON方法处理
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
	return decodeJSON(req.Body, obj)
}

func (jsonBinding) BindBody(body []byte, obj interface{}) error {
	return decodeJSON(bytes.NewReader(body), obj)
}
// 可以看到,这个方法就是从request中获取body然后使用json包的decoder来转换成对象
func decodeJSON(r io.Reader, obj interface{}) error {
	decoder := json.NewDecoder(r)
	if EnableDecoderUseNumber {
		decoder.UseNumber()
	}
	if err := decoder.Decode(obj); err != nil {
		return err
	}
	return validate(obj)
}

最后再看一下context是如何调用的

//这个binding.JSON 就是上面binding中定义的那8个类其中的一个 这里可以直接的使用
func (c *Context) BindJSON(obj interface{}) error {
	return c.MustBindWith(obj, binding.JSON)
}
//这个方法可以接收Binding接口,在Gin中有7 8 个类都是继承了这个接口 都可以调用这个方法来执行解析 
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
	if err = c.ShouldBindWith(obj, b); err != nil {
		c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind)
	}

	return
}
//这个方法就是调用上面一段代码定义的json的bind方法
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
	return b.Bind(c.Request, obj)
}

看了上面的代码,其实整个binding已经很清晰了。 最终,Gin就是调用了json包中的解析。那么我们上面的一系列问题就很轻松的解决了

  • 如果客户端输入的是{"user":"TAO","age":10} 没有输入password 结果{"Tao",10}
  • 如果客户端输入的是{"user":"TAO","age":10,"class":"class"}多输入了一个class的参数 结果{"Tao",10} 如果在login类添加class 这里就会出现class
  • 如果客户端输入的是{} 这边会不会报错 结果{0} 只有age有一个默认值0 其余都是空字符串 这些行为跟json的处理行为是完全一致的。

简单模仿实现

老规矩,在了解了binding的主体流程之后,我们可以自己简单的实现其中几个功能 比如json和xml的解析。我们可以看到以下代码,可以很清晰的看到Gin的实现方式。

package mygin

import (
	"encoding/json"
	"encoding/xml"
	"net/http"
)

var (
	JSON = JsonBinding{}
	XML  = XmlBinding{}
)

//bind接口 方便多态
type Binding interface {
	//返回name
	Name() string
	//具体执行
	Bind(*http.Request, interface{}) error
}

type XmlBinding struct {
}

func (XmlBinding) Name() string {
	return "XmlBinding"
}

func (XmlBinding) Bind(r *http.Request, obj interface{}) error {
	//使用json包中的方法 把string转成对象
	decoder := xml.NewDecoder(r.Body)
	if err := decoder.Decode(obj); err != nil {
		return err
	}
	return nil
}

type JsonBinding struct {
}

func (JsonBinding) Name() string {
	return "jsonBinding"
}

func (JsonBinding) Bind(r *http.Request, obj interface{}) error {
	//使用json包中的方法 把string转成对象
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(obj); err != nil {
		return err
	}

	return nil
}
// context调用
func (c *Context) BindWith(obj interface{}, b Binding) error {
	//调用binding 的Bind方法
	if err := b.Bind(c.Request, obj); err != nil {
		return err
	}

	return nil
}

func (c *Context) BindJson(obj interface{}) error {
	return c.BindWith(obj, JSON)
}

代码比较简单,几乎不需要加注释就能很清楚的看懂。 配合上一篇文章的Gin主流程可以完全的跑起来。结果这里就不写了,上面都描述过了。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

雷军传

雷军传

陈润 / 华中科技大学出版社 / 2013-10-1 / 35

讲述雷军20年中从打工者到职业经理人,再到投资家、创业家的职场历程。 这种经历国内绝无仅有,国内有媒体将其比作乔布斯,事实上他比“乔帮主”更精彩,乔是被董事会赶出苹果,而雷从未失败过。 本书以雷军的职场经历和金山、小米的发展历程和雷军投资故事为主线,以时间为脉络,将其20 年商海沉浮的经历完美展现。 通过故事总结和阐释,讲透用人、管理、营销、战略、投资等全方位的经管知识,从雷军身......一起来看看 《雷军传》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具