Go语言web框架 GIN

  1. gingo语言环境下的一个web框架, 它类似于Martini, 官方声称它比Martini有更好的性能, Martini40倍, Ohhhh….看着不错的样子, 所以就想记录一下gin的学习. gingithub代码在这里: gin源码. gin的效率获得如此突飞猛进, 得益于另一个开源项目httprouter, 项目地址: httprouter源码. 下面主要记录一下gin的使用.
  • 1

1. 安装gin

  1. 使用命令go get github.com/gin-gonic/gin就可以. 我们使用gin的时候引入相应的包就OKimport "github.com/gin-gonic/gin".
  • 1

2. 使用方法

<1> 一种最简单的使用GET/POST方法

gin服务端代码是:

  1. / func1: 处理最基本的GET
  2. func func1 (c *gin.Context) {
  3. // 回复一个200OK,在client的http-get的resp的body中获取数据
  4. c.String(http.StatusOK, "test1 OK")
  5. }
  6. // func2: 处理最基本的POST
  7. func func2 (c *gin.Context) {
  8. // 回复一个200 OK, 在client的http-post的resp的body中获取数据
  9. c.String(http.StatusOK, "test2 OK")
  10. }
  11. func main(){
  12. // 注册一个默认的路由器
  13. router := gin.Default()
  14. // 最基本的用法
  15. router.GET("/test1", func1)
  16. router.POST("/test2", func2)
  17. // 绑定端口是8888
  18. router.Run(":8888")
  19. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

客户端代码是:

  1. func main(){
  2. // 调用最基本的GET,并获得返回值
  3. resp,_ := http.Get("http://0.0.0.0:8888/test1")
  4. helpRead(resp)
  5. // 调用最基本的POST,并获得返回值
  6. resp,_ = http.Post("http://0.0.0.0:8888/test2", "",strings.NewReader(""))
  7. helpRead(resp)
  8. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在服务端, 实例化了一个router, 然后使用GET和POST方法分别注册了两个服务, 当我们使用HTTP GET方法的时候会使用GET注册的函数, 如果使用HTTP POST的方法, 那么会使用POST注册的函数. gin支持所有的HTTP的方法例如: GET, POST, PUT, PATCH, DELETE 和 OPTIONS等. 看客户端中的代码, 当调用http.Get(“http://0.0.0.0:8888/test1“)的时候, 服务端接收到请求, 并根据/test1将请求路由到func1函数进行 处理. 同理, 调用http.Post(“http://0.0.0.0:8888/test2“, “”,strings.NewReader(“”))时候, 会使用func2函数处理. 在func1和func2中, 使用gin.Context填充了一个String的回复. 当然也支持JSON, XML, HTML等其他一些格式数据. 当执行c.String或者c.JSON时, 相当于向http的回复缓冲区写入了 一些数据. 最后调用router.Run(“:8888”)开始进行监听,Run的核心代码是:

  1. func (engine *Engine) Run(addr string) (err error) {
  2. debugPrint("Listening and serving HTTP on %s\n", addr)
  3. defer func() { debugPrintError(err) }()
  4. // 核心代码
  5. err = http.ListenAndServe(addr, engine)
  6. return
  7. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其本质就是http.ListenAndServe(addr, engine). 
注意: helpRead函数是用于读取response的Body的函数, 你可以自己定义, 本文中此函数定义为:

  1. // 用于读取resp的body
  2. func helpRead(resp *http.Response) {
  3. defer resp.Body.Close()
  4. body, err := ioutil.ReadAll(resp.Body)
  5. if err != nil {
  6. fmt.Println("ERROR2!: ", err)
  7. }
  8. fmt.Println(string(body))
  9. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

<2> 传递参数

传递参数有几种方法, 对应到gin使用几种不同的方式来解析.

第一种: 使用gin.Context中的Param方法解析

对应的服务端代码为:

  1. // func3: 处理带参数的path-GET
  2. func func3(c *gin.Context) {
  3. // 回复一个200 OK
  4. // 获取传入的参数
  5. name := c.Param("name")
  6. passwd := c.Param("passwd")
  7. c.String(http.StatusOK, "参数:%s %s test3 OK", name, passwd)
  8. }
  9. // func4: 处理带参数的path-POST
  10. func func4(c *gin.Context) {
  11. // 回复一个200 OK
  12. // 获取传入的参数
  13. name := c.Param("name")
  14. passwd := c.Param("passwd")
  15. c.String(http.StatusOK, "参数:%s %s test4 OK", name, passwd)
  16. }
  17. // func5: 注意':'和'*'的区别
  18. func func5(c *gin.Context) {
  19. // 回复一个200 OK
  20. // 获取传入的参数
  21. name := c.Param("name")
  22. passwd := c.Param("passwd")
  23. c.String(http.StatusOK, "参数:%s %s test5 OK", name, passwd)
  24. }
  25. func main(){
  26. router := gin.Default()
  27. // TODO:注意':'必须要匹配,'*'选择匹配,即存在就匹配,否则可以不考虑
  28. router.GET("/test3/:name/:passwd", func3)
  29. router.POST("/test4/:name/:passwd", func4)
  30. router.GET("/test5/:name/*passwd", func5)
  31. router.Run(":8888")
  32. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

客户端测试代码是:

  1. func main() {
  2. // GET传参数,使用gin的Param解析格式: /test3/:name/:passwd
  3. resp,_ = http.Get("http://0.0.0.0:8888/test3/name=TAO/passwd=123")
  4. helpRead(resp)
  5. // POST传参数,使用gin的Param解析格式: /test3/:name/:passwd
  6. resp,_ = http.Post("http://0.0.0.0:8888/test4/name=PT/passwd=456", "",strings.NewReader(""))
  7. helpRead(resp)
  8. // 注意Param中':'和'*'的区别
  9. resp,_ = http.Get("http://0.0.0.0:8888/test5/name=TAO/passwd=789")
  10. helpRead(resp)
  11. resp,_ = http.Get("http://0.0.0.0:8888/test5/name=TAO/")
  12. helpRead(resp)
  13. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注意上面定义参数的方法有两个辅助符号: ‘:’和’’. 如果使用’:’参数方法, 那么这个参数是必须要匹配的, 例如上面的router.GET(“/test3/:name/:passwd”, func3), 当请求URL是 类似于http://0.0.0.0:8888/test3/name=TAO/passwd=123这样的参会被匹配, 如果是http://0.0.0.0:8888/test3/name=TAO 或者http://0.0.0.0:8888/test3/passwd=123是不能匹配的. 但是如果使用’‘参数, 那么这个参数是可选的. router.GET(“/test5/:name/*passwd”, func5) 可以匹配http://0.0.0.0:8888/test5/name=TAO/passwd=789, 也可以匹配http://0.0.0.0:8888/test5/name=TAO/. 需要注意的一点是, 下面这个URL是不是能够 匹配呢? http://0.0.0.0:8888/test5/name=TAO, 注意TAO后面没有’/’, 这个其实就要看有没有一个路由是到http://0.0.0.0:8888/test5/name=TAO路径的, 如果有, 那么指定的那个函数进行处理, 如果没有http://0.0.0.0:8888/test5/name=TAO会被重定向到http://0.0.0.0:8888/test5/name=TAO/, 然后被当前注册的函数进行处理.

第二种: 使用gin.Context中的Query方法解析

这个类似于正常的URL中的参数传递, 先看服务端代码:

  1. // 使用Query获取参数
  2. func func6(c *gin.Context) {
  3. // 回复一个200 OK
  4. // 获取传入的参数
  5. name := c.Query("name")
  6. passwd := c.Query("passwd")
  7. c.String(http.StatusOK, "参数:%s %s test6 OK", name, passwd)
  8. }
  9. // 使用Query获取参数
  10. func func7(c *gin.Context) {
  11. // 回复一个200 OK
  12. // 获取传入的参数
  13. name := c.Query("name")
  14. passwd := c.Query("passwd")
  15. c.String(http.StatusOK, "参数:%s %s test7 OK", name, passwd)
  16. }
  17. func main(){
  18. router := gin.Default()
  19. // 使用gin的Query参数形式,/test6?firstname=Jane&lastname=Doe
  20. router.GET("/test6", func6)
  21. router.POST("/test7", func7)
  22. router.Run(":8888")
  23. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

客户端测试代码是:

  1. func main() {
  2. // 使用Query获取参数形式/test6?firstname=Jane&lastname=Doe
  3. resp,_ = http.Get("http://0.0.0.0:8888/test6?name=BBB&passwd=CCC")
  4. helpRead(resp)
  5. resp,_ = http.Post("http://0.0.0.0:8888/test7?name=DDD&passwd=EEE", "",strings.NewReader(""))
  6. helpRead(resp)
  7. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这种方法的参数也是接在URL后面, 形如http://0.0.0.0:8888/test6?name=BBB&passwd=CCC服务器可以使用name := c.Query(“name”)这种 方法来解析参数.

第三种: 使用gin.Context中的PostForm方法解析

我们需要将参数放在请求的Body中传递, 而不是URL中. 先看服务端代码:

  1. // 参数是form中获得,即从Body中获得,忽略URL中的参数
  2. func func8(c *gin.Context) {
  3. message := c.PostForm("message")
  4. extra := c.PostForm("extra")
  5. nick := c.DefaultPostForm("nick", "anonymous")
  6. c.JSON(200, gin.H{
  7. "status": "test8:posted",
  8. "message": message,
  9. "nick": nick,
  10. "extra": extra,
  11. })
  12. }
  13. func main(){
  14. router := gin.Default()
  15. // 使用post_form形式,注意必须要设置Post的type,
  16. // 同时此方法中忽略URL中带的参数,所有的参数需要从Body中获得
  17. router.POST("/test8", func8)
  18. router.Run(":8888")
  19. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

客户端代码是:

  1. func main() {
  2. // 使用post_form形式,注意必须要设置Post的type,同时此方法中忽略URL中带的参数,所有的参数需要从Body中获得
  3. resp,_ = http.Post("http://0.0.0.0:8888/test8", "application/x-www-form-urlencoded",strings.NewReader("message=8888888&extra=999999"))
  4. helpRead(resp)
  5. }
  • 1
  • 2
  • 3
  • 4
  • 5

由于我们使用了request Body, 那么就需要指定Body中数据的形式, 此处是form格式, 即application/x-www-form-urlencoded. 常见的几种http提交数据方式有: application/x-www-form-urlencoded; multipart/form-data; application/json; text/xml. 具体使用请google. 
在服务端, 使用message := c.PostForm(“message”)方法解析参数, 然后进行处理.

<3> 传输文件 
下面测试从client传输文件到server. 传输文件需要使用multipart/form-data格式的数据, 所有需要设定Post的类型是multipart/form-data. 
首先看服务端代码:

  1. // 接收client上传的文件
  2. // 从FormFile中获取相关的文件data!
  3. // 然后写入本地文件
  4. func func9(c *gin.Context) {
  5. // 注意此处的文件名和client处的应该是一样的
  6. file, header , err := c.Request.FormFile("uploadFile")
  7. filename := header.Filename
  8. fmt.Println(header.Filename)
  9. // 创建临时接收文件
  10. out, err := os.Create("copy_"+filename)
  11. if err != nil {
  12. log.Fatal(err)
  13. }
  14. defer out.Close()
  15. // Copy数据
  16. _, err = io.Copy(out, file)
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. c.String(http.StatusOK, "upload file success")
  21. }
  22. func main(){
  23. router := gin.Default()
  24. // 接收上传的文件,需要使用
  25. router.POST("/upload", func9)
  26. router.Run(":8888")
  27. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

客户端代码是:

  1. func main() {
  2. // 上传文件POST
  3. // 下面构造一个文件buf作为POST的BODY
  4. buf := new(bytes.Buffer)
  5. w := multipart.NewWriter(buf)
  6. fw,_ := w.CreateFormFile("uploadFile", "images.png") //这里的uploadFile必须和服务器端的FormFile-name一致
  7. fd,_ := os.Open("images.png")
  8. defer fd.Close()
  9. io.Copy(fw, fd)
  10. w.Close()
  11. resp,_ = http.Post("http://0.0.0.0:8888/upload", w.FormDataContentType(), buf)
  12. helpRead(resp)
  13. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

首先客户端本地需要有一张”images.png”图片, 同时需要创建一个Form, 并将field-name命名为”uploadFile”, file-name命名为”images.png”. 在服务端, 通过”uploadFile”可以得到文件信息. 客户端继续将图片数据copy到创建好的Form中, 将数据数据Post出去, 注意数据的类型指定! 在服务端, 通过file, header , err := c.Request.FormFile(“uploadFile”)获得文件信息, file中就是文件数据, 将其拷贝到本地文件, 完成文件传输.

<4> binding数据

gin内置了几种数据的绑定例如JSON, XML等. 简单来说, 即根据Body数据类型, 将数据赋值到指定的结构体变量中. (类似于序列化和反序列化) 
看服务端代码:

  1. // Binding数据
  2. // 注意:后面的form:user表示在form中这个字段是user,不是User, 同样json:user也是
  3. // 注意:binding:"required"要求这个字段在client端发送的时候必须存在,否则报错!
  4. type Login struct {
  5. User string `form:"user" json:"user" binding:"required"`
  6. Password string `form:"password" json:"password" binding:"required"`
  7. }
  8. // bind JSON数据
  9. func funcBindJSON(c *gin.Context) {
  10. var json Login
  11. // binding JSON,本质是将request中的Body中的数据按照JSON格式解析到json变量中
  12. if c.BindJSON(&json) == nil {
  13. if json.User == "TAO" && json.Password == "123" {
  14. c.JSON(http.StatusOK, gin.H{"JSON=== status": "you are logged in"})
  15. } else {
  16. c.JSON(http.StatusUnauthorized, gin.H{"JSON=== status": "unauthorized"})
  17. }
  18. } else {
  19. c.JSON(404, gin.H{"JSON=== status": "binding JSON error!"})
  20. }
  21. }
  22. // 下面测试bind FORM数据
  23. func funcBindForm(c *gin.Context) {
  24. var form Login
  25. // 本质是将c中的request中的BODY数据解析到form中
  26. // 方法一: 对于FORM数据直接使用Bind函数, 默认使用使用form格式解析,if c.Bind(&form) == nil
  27. // 方法二: 使用BindWith函数,如果你明确知道数据的类型
  28. if c.BindWith(&form, binding.Form) == nil{
  29. if form.User == "TAO" && form.Password == "123" {
  30. c.JSON(http.StatusOK, gin.H{"FORM=== status": "you are logged in"})
  31. } else {
  32. c.JSON(http.StatusUnauthorized, gin.H{"FORM=== status": "unauthorized"})
  33. }
  34. } else {
  35. c.JSON(404, gin.H{"FORM=== status": "binding FORM error!"})
  36. }
  37. }
  38. func main(){
  39. router := gin.Default()
  40. // 下面测试bind JSON数据
  41. router.POST("/bindJSON", funcBindJSON)
  42. // 下面测试bind FORM数据
  43. router.POST("/bindForm", funcBindForm)
  44. // 下面测试JSON,XML等格式的rendering
  45. router.GET("/someJSON", func(c *gin.Context) {
  46. c.JSON(http.StatusOK, gin.H{"message": "hey, budy", "status": http.StatusOK})
  47. })
  48. router.GET("/moreJSON", func(c *gin.Context) {
  49. // 注意:这里定义了tag指示在json中显示的是user不是User
  50. var msg struct {
  51. Name string `json:"user"`
  52. Message string
  53. Number int
  54. }
  55. msg.Name = "TAO"
  56. msg.Message = "hey, budy"
  57. msg.Number = 123
  58. // 下面的在client的显示是"user": "TAO",不是"User": "TAO"
  59. // 所以总体的显示是:{"user": "TAO", "Message": "hey, budy", "Number": 123
  60. c.JSON(http.StatusOK, msg)
  61. })
  62. // 测试发送XML数据
  63. router.GET("/someXML", func(c *gin.Context) {
  64. c.XML(http.StatusOK, gin.H{"name":"TAO", "message": "hey, budy", "status": http.StatusOK})
  65. })
  66. router.Run(":8888")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

客户端代码:

  1. func main() {
  2. // 下面测试binding数据
  3. // 首先测试binding-JSON,
  4. // 注意Body中的数据必须是JSON格式
  5. resp,_ = http.Post("http://0.0.0.0:8888/bindJSON", "application/json", strings.NewReader("{\"user\":\"TAO\", \"password\": \"123\"}"))
  6. helpRead(resp)
  7. // 下面测试bind FORM数据
  8. resp,_ = http.Post("http://0.0.0.0:8888/bindForm", "application/x-www-form-urlencoded", strings.NewReader("user=TAO&password=123"))
  9. helpRead(resp)
  10. // 下面测试接收JSON和XML数据
  11. resp,_ = http.Get("http://0.0.0.0:8888/someJSON")
  12. helpRead(resp)
  13. resp,_ = http.Get("http://0.0.0.0:8888/moreJSON")
  14. helpRead(resp)
  15. resp,_ = http.Get("http://0.0.0.0:8888/someXML")
  16. helpRead(resp)
  17. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

客户端发送请求, 在服务端可以直接使用c.BindJSON绑定到Json结构体上. 或者使用BindWith函数也可以, 但是需要指定绑定的数据类型, 例如JSON, XML, HTML等. Bind*函数的本质是读取request中的body数据, 拿BindJSON为例, 其核心代码是:

  1. func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
  2. // 核心代码: decode请求的body到obj中
  3. decoder := json.NewDecoder(req.Body)
  4. if err := decoder.Decode(obj); err != nil {
  5. return err
  6. }
  7. return validate(obj)
  8. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

<5> router group

router group是为了方便前缀相同的URL的管理, 其基本用法如下. 
首先看服务端代码:

  1. // router GROUP - GET测试
  2. func func10(c *gin.Context) {
  3. c.String(http.StatusOK, "test10 OK")
  4. }
  5. func func11(c *gin.Context) {
  6. c.String(http.StatusOK, "test11 OK")
  7. }
  8. // router GROUP - POST测试
  9. func func12(c *gin.Context) {
  10. c.String(http.StatusOK, "test12 OK")
  11. }
  12. func func13(c *gin.Context) {
  13. c.String(http.StatusOK, "test13 OK")
  14. }
  15. func main(){
  16. router := gin.Default()
  17. // router Group是为了将一些前缀相同的URL请求放在一起管理
  18. group1 := router.Group("/g1")
  19. group1.GET("/read1", func10)
  20. group1.GET("/read2", func11)
  21. group2 := router.Group("/g2")
  22. group2.POST("/write1", func12)
  23. group2.POST("/write2", func13)
  24. router.Run(":8888")
  25. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

客户端测试代码:

  1. func main() {
  2. // 下面测试router 的GROUP
  3. resp,_ = http.Get("http://0.0.0.0:8888/g1/read1")
  4. helpRead(resp)
  5. resp,_ = http.Get("http://0.0.0.0:8888/g1/read2")
  6. helpRead(resp)
  7. resp,_ = http.Post("http://0.0.0.0:8888/g2/write1", "", strings.NewReader(""))
  8. helpRead(resp)
  9. resp,_ = http.Post("http://0.0.0.0:8888/g2/write2", "", strings.NewReader(""))
  10. helpRead(resp)
  11. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在服务端代码中, 首先创建了一个组group1 := router.Group(“/g1”), 并在这个组下注册了两个服务, group1.GET(“/read1”, func10) 和group1.GET(“/read2”, func11), 那么当使用http://0.0.0.0:8888/g1/read1http://0.0.0.0:8888/g1/read2访问时, 是可以路由 到上面注册的位置的. 同理对于group2 := router.Group(“/g2”)也是一样的.

<6> 静态文件服务

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

  1. func main(){
  2. router := gin.Default()
  3. // 下面测试静态文件服务
  4. // 显示当前文件夹下的所有文件/或者指定文件
  5. router.StaticFS("/showDir", http.Dir("."))
  6. router.Static("/files", "/bin")
  7. router.StaticFile("/image", "./assets/1.png")
  8. router.Run(":8888")
  9. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

首先你需要在服务器的路径下创建一个assert文件夹, 并且放入1.png文件. 如果已经存在, 请忽略. 
测试代码: 请在浏览器中输入0.0.0.0:8888/showDir, 显示的是服务器当前路径下地文件信息:

1

输入0.0.0.0:8888/files, 显示的是/bin目录下地文件信息:

2

输入0.0.0.0:8888/image, 显示的是服务器下地./assets/1.png图片:

3

<7> 加载模板templates

gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据. 
看服务端代码

  1. func main(){
  2. router := gin.Default()
  3. // 下面测试加载HTML: LoadHTMLTemplates
  4. // 加载templates文件夹下所有的文件
  5. router.LoadHTMLGlob("templates/*")
  6. // 或者使用这种方法加载也是OK的: router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
  7. router.GET("/index", func(c *gin.Context) {
  8. // 注意下面将gin.H参数传入index.tmpl中!也就是使用的是index.tmpl模板
  9. c.HTML(http.StatusOK, "index.tmpl", gin.H{
  10. "title": "GIN: 测试加载HTML模板",
  11. })
  12. })
  13. router.Run(":8888")
  14. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

客户端测试代码是:

  1. func main() {
  2. // 测试加载HTML模板
  3. resp,_ = http.Get("http://0.0.0.0:8888/index")
  4. helpRead(resp)
  5. }
  • 1
  • 2
  • 3
  • 4
  • 5

在服务端, 我们需要加载需要的templates, 这里有两种方法: 第一种使用LoadHTMLGlob加载所有的正则匹配的模板, 本例中使用的是*, 即匹配所有文件, 所以加载的是 templates文件夹下所有的模板. 第二种使用LoadHTMLFiles加载指定文件. 在本例服务器路径下有一个templates目录, 下面有一个index.tmpl模板, 模板的 内容是:

  1. <html>
  2. <h1>
  3. { { .title } }
  4. </h1>
  5. </html>
  • 1
  • 2
  • 3
  • 4
  • 5

当客户端请求/index时, 服务器使用这个模板, 并填充相应的参数, 此处参数只有title, 然后将HTML数据返回给客户端. 
你也可以在浏览器请求0.0.0.0:8888/index, 效果如下图所示: 

<8> 重定向

重定向相对比较简单, 服务端代码是:

  1. func main(){
  2. router := gin.Default()
  3. // 下面测试重定向
  4. router.GET("/redirect", func(c *gin.Context) {
  5. c.Redirect(http.StatusMovedPermanently, "http://shanshanpt.github.io/")
  6. })
  7. router.Run(":8888")
  8. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

客户端测试代码是:

  1. func main() {
  2. // 下面测试重定向
  3. resp,_ = http.Get("http://0.0.0.0:8888/redirect")
  4. helpRead(resp)
  5. }
  • 1
  • 2
  • 3
  • 4
  • 5

当我们请求http://0.0.0.0:8888/redirect的时候, 会重定向到http://shanshanpt.github.io/这个站点.

<9> 使用middleware

这里使用了两个例子, 一个是logger, 另一个是BasiAuth, 具体看服务器代码:

  1. func Logger() gin.HandlerFunc {
  2. return func(c *gin.Context) {
  3. t := time.Now()
  4. // 设置example变量到Context的Key中,通过Get等函数可以取得
  5. c.Set("example", "12345")
  6. // 发送request之前
  7. c.Next()
  8. // 发送request之后
  9. latency := time.Since(t)
  10. log.Print(latency)
  11. // 这个c.Write是ResponseWriter,我们可以获得状态等信息
  12. status := c.Writer.Status()
  13. log.Println(status)
  14. }
  15. }
  16. func main(){
  17. router := gin.Default()
  18. // 1
  19. router.Use(Logger())
  20. router.GET("/logger", func(c *gin.Context) {
  21. example := c.MustGet("example").(string)
  22. log.Println(example)
  23. })
  24. // 2
  25. // 下面测试BasicAuth()中间件登录认证
  26. //
  27. var secrets = gin.H{
  28. "foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
  29. "austin": gin.H{"email": "austin@example.com", "phone": "666"},
  30. "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
  31. }
  32. // Group using gin.BasicAuth() middleware
  33. // gin.Accounts is a shortcut for map[string]string
  34. authorized := router.Group("/admin", gin.BasicAuth(gin.Accounts{
  35. "foo": "bar",
  36. "austin": "1234",
  37. "lena": "hello2",
  38. "manu": "4321",
  39. }))
  40. // 请求URL: 0.0.0.0:8888/admin/secrets
  41. authorized.GET("/secrets", func(c *gin.Context) {
  42. // get user, it was set by the BasicAuth middleware
  43. user := c.MustGet(gin.AuthUserKey).(string)
  44. if secret, ok := secrets[user]; ok {
  45. c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
  46. } else {
  47. c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
  48. }
  49. })
  50. router.Run(":8888")
  51. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

客户端测试代码是:

  1. func main() {
  2. // 下面测试使用中间件
  3. resp,_ = http.Get("http://0.0.0.0:8888/logger")
  4. helpRead(resp)
  5. // 测试验证权限中间件BasicAuth
  6. resp,_ = http.Get("http://0.0.0.0:8888/admin/secrets")
  7. helpRead(resp)
  8. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

服务端使用Use方法导入middleware, 当请求/logger来到的时候, 会执行Logger(), 并且我们知道在GET注册的时候, 同时注册了匿名函数, 所有请看Logger函数中存在一个c.Next()的用法, 它是取出所有的注册的函数都执行一遍, 然后再回到本函数中, 所以, 本例中相当于是先执行了 c.Next()即注册的匿名函数, 然后回到本函数继续执行. 所以本例的Print的输出顺序是: 
log.Println(example) 
log.Print(latency) 
log.Println(status) 
如果将c.Next()放在log.Print(latency)后面, 那么log.Println(example)和log.Print(latency)执行的顺序就调换了. 所以一切都取决于c.Next()执行的位置. c.Next()的核心代码如下:

  1. // Next should be used only in the middlewares.
  2. // It executes the pending handlers in the chain inside the calling handler.
  3. // See example in github.
  4. func (c *Context) Next() {
  5. c.index++
  6. s := int8(len(c.handlers))
  7. for ; c.index < s; c.index++ {
  8. c.handlers[c.index](c)
  9. }
  10. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

它其实是执行了后面所有的handlers. 
关于使用gin.BasicAuth() middleware, 可以直接使用一个router group进行处理, 本质和logger一样.

<10> 绑定http server

之前所有的测试中, 我们都是使用router.Run(“:8888”)开始执行监听, 其实还有两种方法:

  1. // 方法二
  2. http.ListenAndServe(":8888", router)
  3. // 方法三:
  4. server := &http.Server{
  5. Addr: ":8888",
  6. Handler: router,
  7. ReadTimeout: 10 * time.Second,
  8. WriteTimeout: 10 * time.Second,
  9. MaxHeaderBytes: 1 << 20,
  10. }
  11. server.ListenAndServe()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

至此, gin最基本的一些应用都整理完了, 下面就具体看看代码中的一些实现. 有时间再记录吧. 
写几个注意事项把: 
1.中间件的使用插拔可以这样写:router.Use(MymiddleWare),写在该语句后面的路由转发,都会经过中间件的过滤,之前的不受影响,对单个路由转发使用一个或者多个中间件可以这样写 router.GET(“/”,MymiddleWare1,MymiddleWare2,HandleFunc) 
2.对c *gin.Context的c.Query(),c.Param()都只适用于GET请求的地址后的参数,如果要接收POST数据,必须使用c.Bind(&buf)

3.参考:

gin-github

Go语言web框架 gin的更多相关文章

  1. GO语言web框架Gin之完全指南

    GO语言web框架Gin之完全指南 作为一款企业级生产力的web框架,gin的优势是显而易见的,高性能,轻量级,易用的api,以及众多的使用者,都为这个框架注入了可靠的因素.截止目前为止,github ...

  2. GO语言web框架Gin之完全指南(一)

    作为一款企业级生产力的web框架,gin的优势是显而易见的,高性能,轻量级,易用的api,以及众多的使用者,都为这个框架注入了可靠的因素.截止目前为止,github上面已经有了 35,994 star ...

  3. GO语言web框架Gin之完全指南(二)

    这篇主要讲解自定义日志与数据验证 参数验证 我们知道,一个请求完全依赖前端的参数验证是不够的,需要前后端一起配合,才能万无一失,下面介绍一下,在Gin框架里面,怎么做接口参数验证的呢 gin 目前是使 ...

  4. 最好的6个Go语言Web框架

    原文:Top 6 web frameworks for Go as of 2017 作者:Edward Marinescu 译者:roy 译者注:本文介绍截至目前(2017年)最好的6个Go语言Web ...

  5. Go语言Web框架gwk介绍4

    Go语言Web框架gwk介绍 (四)   事件 gwk支持事件系统,但并没有硬编码有哪些事件,而是采用了比较松散的定义方式. 订阅事件有两种方式: 调用On函数或者OnFunc函数 func On(m ...

  6. Go语言Web框架gwk介绍 3

    Go语言Web框架gwk介绍 (三)   上一篇忘了ChanResult ChanResult 可以用来模拟BigPipe,定义如下 type ChanResult struct { Wait syn ...

  7. Go语言Web框架gwk介绍2

    Go语言Web框架gwk介绍 (二) HttpResult 凡是实现了HttpResult接口的对象,都可以作为gwk返回Web客户端的内容.HttpResult接口定义非常简单,只有一个方法: ty ...

  8. Go语言Web框架gwk介绍 1

    Go语言Web框架gwk介绍 (一)   今天看到Golang排名到前30名了,看来关注的人越来越多了,接下来几天详细介绍Golang一个web开发框架GWK. 现在博客园支持markdown格式发布 ...

  9. Go组件学习——Web框架Gin

    以前学Java的时候,和Spring全家桶打好关系就行了,从Spring.Spring MVC到SpringBoot,一脉相承. 对于一个Web项目,使用Spring MVC,就可以基于MVC的思想开 ...

随机推荐

  1. 使用using current logfile实现DG备库实时更新

    DG1是主库,DG2是备库:运行在最大可用模式. 实验一:未使用using current logfile参数时,备库在最大可用模式下,不能实时同步备库的更新 -此时需要等待主库进行归档---侧面证明 ...

  2. HDU 4597

    题目大意: 两人轮流从两堆牌从抽取最顶端或者最底部的牌,得到的分数加到自己身上,问先拿牌的最多能得多少分 记忆化搜索,2堆牌的底和顶,有四种方法,根据四种方法来找到最优解 #include <c ...

  3. [Vijos1617] 超级教主(DP + 单调队列)

    传送门 设 f[i] 表示吃完 f[i] 及其以下的能量球后所剩下的能量. 所以 f[i] = max(f[i], f[j] + (sum[i] - sum[j]) - i * 100) ( 0 &l ...

  4. WebApi下载附件文件

    WebApi下载附件文件 1. [RoutePrefix("down")] public class FilesController : ApiController { [GET( ...

  5. Codevs 3556 科技庄园==洛谷 P2760

    时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题目描述 Description Life是codevs的用户,他是一个道德极高的用户,他积极贯彻党的十八大精神, ...

  6. Windows Server 2003的一些优化设置 (转至网络)

    2003序列号:JCHKR-888KX-27HVK-DT88X-T767M1.禁用配置服务器向导: 禁止“配置你的服务器”(Manage Your Server)向导的出现:在控制面板(Control ...

  7. IDEA下使用protobuf2(java)

    目录 一.介绍 二.特点 三.结构 四.选择版本 五.Intellij IDEA中使用Protobuf 1.下载个protoc.exe 2.编辑个.proto文件 3.将.proto文件转成Java类 ...

  8. java Map集合对比分析

    1.Map:Map是所有map集合的顶级父接口,用于key/value形式的键值对,其中每一个key都映射到一个值,key不能重复. 2.TreeMap:该map将存储的键值对进行默认排序,并且还能够 ...

  9. Spring基于注解的配置概述

    以下内容引用自http://wiki.jikexueyuan.com/project/spring/annotation-based-configuration.html: 从Spring 2.5开始 ...

  10. Win7 Windows Update更新的文件默认在哪个位置

    C:\Windows\SoftwareDistribution\download