本文共 3873 字,大约阅读时间需要 12 分钟。
Gin
是使用Go语言编写的高性能的web
服务框架,根据官方的测试,性能是httprouter
的40倍左右。要使用好这套框架呢,首先我们就得对这个框架的基本结构有所了解,所以我将从以下几个方面来对Gin
的源码进行解读。
Gin
是如何储存和映射URL
路径到相应的处理函数的Gin
中间件的设计思想及其实现Gin
是如何解析客户端发送请求中的参数的Gin
是如何将各类格式(JSON/XML/YAML
等)数据解析返回的在看完前面三章之后,我们大致了解了Gin
是如何工作、如果接收客户端请求的,但是对于一个Web
框架还有一个最重要的特性的就是处理完请求后返回相应的数据。围绕着这一点,我们来看下Gin
是如何解析各类数据结构的。
老规矩,我们还是以官方的示例代码来逐步分析:
func main() { router := gin.Default() router.POST("/something", func(c *gin.Context) { c.JSON(200, gin.H{ "status": "posted", "message": "message", "nick": "nick", }) c.XML(200,gin.H{ "status": "posted", "nick": "nick", }) c.String(http.StatusOK, "hello world") }) if err := router.Run();err != nil { log.Println("something error"); }}
上面这个示例中我们返回了三种常见格式的数据JSON/XML/String
,进入这三个方法我们可以看到:
func (c *Context) JSON(code int, obj interface{ }) { c.Render(code, render.JSON{ Data: obj})}func (c *Context) XML(code int, obj interface{ }) { c.Render(code, render.XML{ Data: obj})}func (c *Context) String(code int, format string, values ...interface{ }) { c.Render(code, render.String{ Format: format, Data: values})}
这三个方法,都是调用了Context
的Render
方法,那么我们继续追踪:
func (c *Context) Render(code int, r render.Render) { c.Status(code) if !bodyAllowedForStatus(code) { r.WriteContentType(c.Writer) c.Writer.WriteHeaderNow() return } if err := r.Render(c.Writer); err != nil { panic(err) }}func (c *Context) Status(code int) { c.Writer.WriteHeader(code)}
可以看到这个方法就是一个标准的代理模式
,Render
负责传入代理对象,COntext
负责执行代理对象的render.Render
接口方法,来向c.Writer
写出数据。我们先来看一下render.Render
接口的原型:
type Render interface { //负责写出用户指定的数据 Render(http.ResponseWriter) error //负责向头部写出ContentType的值 WriteContentType(w http.ResponseWriter)}
我们以JSON
为例,来看一下该数据类型的实现方法是怎样的:
func (r JSON) Render(w http.ResponseWriter) (err error) { if err = WriteJSON(w, r.Data); err != nil { panic(err) } return}//这个就是JSON格式的ContentTypevar jsonContentType = []string{ "application/json; charset=utf-8"}var jsonpContentType = []string{ "application/javascript; charset=utf-8"}var jsonAsciiContentType = []string{ "application/json"}func WriteJSON(w http.ResponseWriter, obj interface{ }) error { writeContentType(w, jsonContentType) //这里调用的是GO SDK中的序列化函数获取的字节数组 jsonBytes, err := json.Marshal(obj) if err != nil { return err } _, err = w.Write(jsonBytes) return err}//这个函数就是将指定的ContentType,设置到响应的头部func writeContentType(w http.ResponseWriter, value []string) { header := w.Header() if val := header["Content-Type"]; len(val) == 0 { header["Content-Type"] = value }}//注意这个函数和调用那个函数的首字母大小写func (r JSON) WriteContentType(w http.ResponseWriter) { writeContentType(w, jsonContentType)}
整个过程很简单,没有什么需要多说的,总体概况就两点:写出字节流
和写出头部信息
要想查看Gin
支持哪些数据格式,只需要哪些数据格式实现了Render
接口,不过Gin
官方已经在Render
包中为我们罗列出来了:
var ( _ Render = JSON{ } _ Render = IndentedJSON{ } _ Render = SecureJSON{ } _ Render = JsonpJSON{ } _ Render = XML{ } _ Render = String{ } _ Render = Redirect{ } _ Render = Data{ } _ Render = HTML{ } _ HTMLRender = HTMLDebug{ } _ HTMLRender = HTMLProduction{ } _ Render = YAML{ } _ Render = Reader{ } _ Render = AsciiJSON{ } _ Render = ProtoBuf{ })
使用方法也很明了:
router.POST("/upload", func(c *gin.Context) { //假如我要返回json数据调用JSON方法即可 c.JSON(200, gin.H{ "status": "posted", "message": "message", "nick": "nick", }) //假如我要返回XML数据调用XML方法即可 c.XML(200,gin.H{ "status": "posted", "nick": "nick", }) //其他类似})
除了上面的常用数据格式之外,还有就是返回文件流,这个调用的是c.File("filepath")
,这个方法的原型是这样的:
// 这个功能是由http包提供的func (c *Context) File(filepath string) { http.ServeFile(c.Writer, c.Request, filepath)}
不仅是c.File()
方法,而且静态文件路由router.Static("/assets", "./assets")
也是最终调用的http.ServeFile
,这个方法就不剖析了,涵盖点有点多,这个方法的大致作用就是根据文件的信息,设置了响应头部信息,然后再将文件输出到响应流中。
转载地址:http://pqfen.baihongyu.com/