示例

1. AsciiJSON

使用 AsciiJSON 对非Ascii码的字符转义为Unicode

  1. func main() {
  2. r := gin.Default()
  3. r.GET("/someJSON", func(c *gin.Context) {
  4. data := map[string]interface{}{
  5. "lang语言": "GO语言",
  6. "tag1212": "<br>",
  7. }
  8. // 在这里会对中文以及特殊符号处理 -- 处理为Unicode编码形式
  9. c.AsciiJSON(http.StatusOK, data)
  10. })
  11. r.Run(":8080")
  12. }

http返回结果为:

  1. {"lang\u8bed\u8a00":"GO\u8bed\u8a00","tag1212":"\u003cbr\u003e"}

2. 绑定表单数据到自定义结构体

Bind form-data request with custom struct

数据结构

  1. type StructA struct {
  2. FieldA string `form:"field_a"`
  3. }
  4. type StructB struct {
  5. NestedStruct StructA
  6. FieldB string `form:"field_b"`
  7. }
  8. type StructC struct {
  9. NestedStructPointer *StructA
  10. FieldC string `form:"field_c"`
  11. }
  12. type StructD struct {
  13. NestedAnonyStruct struct {
  14. FieldX string `form:"field_x"`
  15. }
  16. FieldD string `form:"field_d"`
  17. }
  18. func GetDataB(c *gin.Context) {
  19. var b StructB
  20. c.Bind(&b)
  21. c.JSON(200, gin.H{
  22. "a": b.NestedStruct,
  23. "b": b.FieldB,
  24. })
  25. }
  26. func GetDataC(c *gin.Context) {
  27. var b StructC
  28. c.Bind(&b)
  29. c.JSON(200, gin.H{
  30. "a": b.NestedStructPointer,
  31. "c": b.FieldC,
  32. })
  33. }
  34. func GetDataD(c *gin.Context) {
  35. var b StructD
  36. c.Bind(&b)
  37. c.JSON(200, gin.H{
  38. "x": b.NestedAnonyStruct,
  39. "d": b.FieldD,
  40. })
  41. }

示例1

  1. http://localhost:8080/getb?field_a=hello&field_b=world

在getb中绑定的是结构体B (字段分别为结构体A(field_a)和field_b)
返回结果:

  1. {"a":{"FieldA":"hello"},"b":"world"}

示例2

  1. http://localhost:8080/getc?field_a=hello&field_c=world

在getb中绑定的是结构体C(字段分别为结构体A(field_a)和field_c)
返回结果:

  1. {"a":{"FieldA":"hello"},"c":"world"}

示例3

  1. http://localhost:8080/getd?field_x=hello&field_d=world

在getd中绑定的是结构体D(字段分别为新定义的结构体A(field_a)和field_d)
返回结果:

  1. {"d":"world","x":{"FieldX":"hello"}}

同理只要把相应的方法改为Post 参数以form-data的形式在表单传入效果相同

3. 绑定查询字符串或表单数据

Bind query string or post data

数据结构

  1. package main
  2. import (
  3. "log"
  4. "time"
  5. "github.com/gin-gonic/gin"
  6. )
  7. // 目标结构体
  8. type Person struct {
  9. Name string `form:"name"`
  10. Address string `form:"address"`
  11. Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
  12. }
  13. func main() {
  14. route := gin.Default()
  15. route.GET("/testingg", startPage)
  16. route.POST("/testingp", startPage)
  17. route.Run(":8080")
  18. }
  19. func startPage(c *gin.Context) {
  20. var person Person
  21. if c.ShouldBind(&person) == nil {
  22. log.Println(person.Name)
  23. log.Println(person.Address)
  24. log.Println(person.Birthday)
  25. }
  26. c.JSON(200, gin.H{
  27. "name": person.Name,
  28. "address": person.Address,
  29. "birthday": person.Birthday,
  30. })
  31. }

Get

直接在路由上加参数 如 http://localhost:8080/testingg?name=appleboy&address=xyz&birthday=1992-03-15
返回数据:

  1. {"address":"xyz","birthday":"1992-03-15T00:00:00Z","name":"appleboy"}

需注意的是 参数名称必须和结构体字段的Tag一致
如路由为http://localhost:8080/testingg?Name=appleboy&address=xyz&birthday=1992-03-15
返回数据为:

  1. {"address":"xyz","birthday":"1992-03-15T00:00:00Z","name":""}

4. 绑定URI

数据结构:

  1. package main
  2. import "github.com/gin-gonic/gin"
  3. type Person struct {
  4. ID string `uri:"id" binding:"required,uuid"`
  5. Name string `uri:"name" binding:"required"`
  6. }
  7. func main() {
  8. route := gin.Default()
  9. route.GET("/:name/:id", func(c *gin.Context) {
  10. var person Person
  11. if err := c.ShouldBindUri(&person); err != nil {
  12. c.JSON(400, gin.H{"msg": err})
  13. return
  14. }
  15. c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
  16. })
  17. route.Run(":8080")
  18. }

对参数进行格式校验,以及是否必须进行校验 ,只要是路径不匹配,绑定都会失败

5. 自定义Http配置

直接使用ListenAndServer

  1. router := gin.Default()
  2. router.GET("/get", func(c *gin.Context) {
  3. c.JSON(200, "Success")
  4. })
  5. http.ListenAndServe(":8080", router)

或自定义进行配置

  1. router := gin.Default()
  2. router.GET("/get", func(c *gin.Context) {
  3. c.JSON(200, "Success")
  4. })
  5. s := &http.Server{
  6. Addr: ":8080",
  7. Handler: router,
  8. ReadTimeout: 10 * time.Second,
  9. WriteTimeout: 10 * time.Second,
  10. MaxHeaderBytes: 1 << 20,
  11. }
  12. s.ListenAndServe()

6. Html渲染

数据直接返回到指定的模板文件

  1. func main() {
  2. router := gin.Default()
  3. router.LoadHTMLGlob("./templates/*")
  4. router.GET("/index", func(c *gin.Context) {
  5. c.HTML(http.StatusOK, "index.tmpl", gin.H{
  6. "title": "Main website",
  7. })
  8. })
  9. router.Run(":8080")
  10. }
  1. <html>
  2. <h1>
  3. {{ .title }}
  4. </h1>
  5. </html>

7. 记录日志

  1. func main() {
  2. gin.DisableConsoleColor()
  3. f, _ := os.Create("gin.log")
  4. gin.DefaultWriter = io.MultiWriter(f)
  5. router := gin.Default()
  6. router.GET("/ping", func(c *gin.Context) {
  7. c.String(200, "pong")
  8. })
  9. router.Run(":8080")
  10. }

在请求时可以查看到指定路径的日志文件里面写入的日志

  1. [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
  2. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
  3. - using env: export GIN_MODE=release
  4. - using code: gin.SetMode(gin.ReleaseMode)
  5. [GIN-debug] GET /ping --> main.main.func1 (3 handlers)
  6. [GIN-debug] Listening and serving HTTP on :8080
  7. [GIN] xxxxxxx | 200 | 1.0036ms | ::1 | GET "/ping"
  8. [GIN] xxxxxxx | 200 | 0s | ::1 | GET "/ping"

8. XML/JSON/YAML/ProtoBuf 渲染

Json渲染

使用简单数据

  1. func main() {
  2. r := gin.Default()
  3. // gin.H 是一个类型为 map[string]interface{}的数据格式
  4. r.GET("/json", func(c *gin.Context) {
  5. c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
  6. })
  7. r.Run(":8080")
  8. }

返回数据为

  1. {"message":"hey","status":200}

使用结构体

  1. func main() {
  2. r := gin.Default()
  3. r.GET("/sjson", func(c *gin.Context) {
  4. // 使用结构体 并返回Json
  5. var msg struct {
  6. Name string `json:"user"`
  7. Message string
  8. Number int
  9. }
  10. msg.Name = "Lena"
  11. msg.Message = "hey"
  12. msg.Number = 123
  13. // 最终返回的键都是已Tag为标准
  14. c.JSON(http.StatusOK, msg)
  15. })
  16. r.Run(":8080")
  17. }

返回结果为:

  1. {"user":"Lena","Message":"hey","Number":123}

XML渲染

  1. func main() {
  2. r := gin.Default()
  3. r.GET("/xml", func(c *gin.Context) {
  4. c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
  5. })
  6. r.Run(":8080")
  7. }

返回结果为:

  1. <map>
  2. <message>hey</message
  3. ><status>200</status>
  4. </map>

Yaml

  1. func main() {
  2. r := gin.Default()
  3. r.GET("/yaml", func(c *gin.Context) {
  4. c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
  5. })
  6. r.Run(":8080")
  7. }

返回结果为:
直接为yaml文件

ProtoBuf

待完善

9. 中间件的使用

日志中间件使用示例

  1. func main() {
  2. // 新建一个没有任何默认中间件的路由
  3. r := gin.New()
  4. // LLogger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。
  5. gin.DefaultWriter = os.Stdout // 控制台输出
  6. r.Use(gin.Logger())
  7. r.GET("/ping", func(c *gin.Context) {
  8. c.String(200, "pong")
  9. })
  10. r.Run(":8080")
  11. }

使用中间件后控制台可以看到输出情况

  1. [GIN-debug] Listening and serving HTTP on :8080
  2. [GIN] xxxxx - 17:53:15 | 200 | 2.1258ms | ::1 | GET "/ping"
  3. [GIN] xxxxx - 17:53:16 | 200 | 0s | ::1 | GET "/ping"

对指定的路由使用指定的中间件

  1. func main() {
  2. // 新建一个没有任何默认中间件的路由
  3. r := gin.New()
  4. // 对于特定的路由可以指定多个中间件
  5. r.GET("/benchmark",gin.Logger())
  6. r.GET("/ping",gin.Logger(),func(c *gin.Context) {
  7. c.String(200, "pong")
  8. })
  9. r.Run(":8080")
  10. }

使用 BasicAuth 中间件

  1. // 简单的用户信息
  2. var secrets = gin.H{
  3. "foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
  4. "austin": gin.H{"email": "austin@example.com", "phone": "666"},
  5. "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
  6. }
  7. func main() {
  8. r := gin.Default()
  9. // 路由组使用BasicAuth中间件
  10. authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
  11. "foo": "bar",
  12. "austin": "1234",
  13. "lena": "hello2",
  14. "manu": "4321",
  15. }))
  16. authorized.GET("/secrets", func(c *gin.Context) {
  17. user := c.MustGet(gin.AuthUserKey).(string)
  18. fmt.Println(user, "-------")
  19. if secret, ok := secrets[user]; ok {
  20. c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
  21. } else {
  22. c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
  23. }
  24. })
  25. r.Run(":8080")
  26. }

说明:
当请求头中没有Authorization或者是Authorization值无效时,需要登陆

10. 请求参数映射为Map

Map as querystring or postform parameters

  1. func main() {
  2. router := gin.Default()
  3. router.POST("/post", func(c *gin.Context) {
  4. // 把在路由中ids的键作为map的键
  5. ids := c.QueryMap("ids") // 从url中传入
  6. names := c.PostFormMap("names") // 从form-data中传入
  7. fmt.Println("---------------")
  8. fmt.Printf("ids: %v; names: %v\n", ids, names)
  9. fmt.Println("---------------")
  10. })
  11. router.Run(":8080")
  12. }

image.png
输出结果为: ids: map[a:1234 b:hello]; names: map[first:thinkerou second:tianou]
简单来说该功能就是允许参数有不同的取值并且在后台可以映射为Map,方便其他操作

11. 获取字符串参数

Query string parameters

  1. func main() {
  2. router := gin.Default()
  3. router.GET("/welcome", func(c *gin.Context) {
  4. firstname := c.DefaultQuery("firstname", "Guest") // 如果没有会有默认值
  5. lastname := c.Query("lastname") // 没有默认值
  6. c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
  7. })
  8. router.Run(":8080")
  9. }

请求地址为 http://localhost:8080/welcome?firstname=454&lastname=89898
返回结果为:

  1. Hello 454 89898

12. 路由组

Grouping routes

  1. func main() {
  2. router := gin.Default()
  3. v1 := router.Group("/v1")
  4. {
  5. v1.POST("/login", func(c *gin.Context) {
  6. c.String(http.StatusOK, "Hello login1")
  7. })
  8. v1.POST("/submit", func(c *gin.Context) {
  9. c.String(http.StatusOK, "Hello submit1")
  10. })
  11. v1.POST("/read", func(c *gin.Context) {
  12. c.String(http.StatusOK, "Hello read1")
  13. })
  14. }
  15. v2 := router.Group("/v2")
  16. {
  17. v2.POST("/login", func(c *gin.Context) {
  18. c.String(http.StatusOK, "Hello login2")
  19. })
  20. v2.POST("/submit", func(c *gin.Context) {
  21. c.String(http.StatusOK, "Hello submit2")
  22. })
  23. v2.POST("/read", func(c *gin.Context) {
  24. c.String(http.StatusOK, "Hello read2")
  25. })
  26. }
  27. router.Run(":8080")
  28. }

这样组织路由,就可以对路由尽心分组/或者按版本进行管理了

  1. http://localhost:8080/v2/read/
  2. http://localhost:8080/v2/read/

13. 路由参数

Parameters in path — 匹配路由中的参数(动态路由)

  1. func main() {
  2. router := gin.Default()
  3. // /user/:name/ 只能匹配 /user/{parms} 或 /user/{param}/ 其他无法匹配
  4. // /user/:name 只能匹配 /user/{parms} 或 /user/{param}/ 其他无法匹配
  5. // router.GET("/user/:name", func(c *gin.Context) {
  6. // name := c.Param("name")
  7. // fmt.Println("name--", name)
  8. // c.String(http.StatusOK, "Hello %s", name)
  9. // })
  10. // 会匹配到 /user/{param}/ 但是不会匹配到/user/{param}
  11. router.GET("/user/:name/*action", func(c *gin.Context) {
  12. name := c.Param("name")
  13. action := c.Param("action")
  14. message := name + " is " + action
  15. c.String(http.StatusOK, message)
  16. })
  17. router.Run(":8080")
  18. }

14. 重定向

Redirects

  1. func main() {
  2. router := gin.Default()
  3. // 重定向外部路由
  4. router.GET("/test", func(c *gin.Context) {
  5. c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
  6. })
  7. // 重定向内部路由 访问test1 执行test2
  8. router.GET("/test1", func(c *gin.Context) {
  9. c.Request.URL.Path = "/test2"
  10. router.HandleContext(c)
  11. })
  12. router.GET("/test2", func(c *gin.Context) {
  13. c.JSON(200, gin.H{"hello": "world"})
  14. })
  15. router.Run(":8080")
  16. }

15. PureJSON

通常,JSON用它们的unicode实体替换特殊的HTML字符,例如<变成\u003c。如果需要原样输出,则可以使用PureJSON。此功能在Go 1.6及更低版本中不可用。

  1. func main() {
  2. r := gin.Default()
  3. // 提供 unicode 实体
  4. r.GET("/json", func(c *gin.Context) {
  5. c.JSON(200, gin.H{
  6. "html": "<b>Hello, world!</b>",
  7. })
  8. })
  9. // 提供字面字符
  10. r.GET("/purejson", func(c *gin.Context) {
  11. c.PureJSON(200, gin.H{
  12. "html": "<b>Hello, world!</b>",
  13. })
  14. })
  15. // 监听并在 0.0.0.0:8080 上启动服务
  16. r.Run(":8080")
  17. }

访问json返回结果:

  1. {"html":"\u003cb\u003eHello, world!\u003c/b\u003e"}

访问purejson返回结果:

  1. {"html":"<b>Hello, world!</b>"}

16. SecureJSON

使用 SecureJSON 防止 json 劫持。 因为JSON数组默认为是可执行的JS。如果给定的结构是数组值,则默认预置 "while(1)," 到响应体。

  1. func main() {
  2. r := gin.Default()
  3. // 你也可以使用自己的 SecureJSON 前缀
  4. r.SecureJsonPrefix("demo',\n")
  5. r.GET("/someJSON", func(c *gin.Context) {
  6. names := []string{"lena", "austin", "foo"}
  7. // 将输出:demo;["lena","austin","foo"]
  8. c.SecureJSON(http.StatusOK, names)
  9. })
  10. r.Run(":8080")
  11. }

返回数据:

  1. demo',
  2. [
  3. "lena",
  4. "austin",
  5. "foo"
  6. ]

17. JSONP

使用 JSONP 向不同域的服务器请求数据。如果查询参数存在回调,则将回调添加到响应体中(达到跨域的目)

  1. func main() {
  2. r := gin.Default()
  3. r.GET("/jsonp", func(c *gin.Context) {
  4. data := map[string]interface{}{
  5. "foo": "bar",
  6. }
  7. // callback 是 x
  8. c.JSONP(http.StatusOK, data)
  9. })
  10. r.Run(":8080")
  11. }

返回结果为:

  1. x({"foo":"bar"});
  2. // 服务端返回对应的JS类型的字符串,被浏览器执行,达到跨域的目的。

18. 从Reader中保存数据

  1. Serving data from reader
  1. func main() {
  2. router := gin.Default()
  3. router.GET("/someDataFromReader", func(c *gin.Context) {
  4. response, err := http.Get("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1594809745162&di=458d6704506ab2b9fa7bbbee9fa5fcda&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg")
  5. if err != nil || response.StatusCode != http.StatusOK {
  6. c.Status(http.StatusServiceUnavailable)
  7. return
  8. }
  9. reader := response.Body
  10. contentLength := response.ContentLength
  11. contentType := response.Header.Get("Content-Type")
  12. extraHeaders := map[string]string{
  13. "Content-Disposition": `attachment; filename="gopher.png"`,
  14. }
  15. fmt.Println("contentLength----", contentLength, "contentType---", contentType, "reader---", reader, "extraHeaders----", extraHeaders)
  16. c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
  17. })
  18. router.Run(":8080")
  19. }

访问请求后,就会下载服务端的数据并直接下载保存在本地

19. 使用 HTTP 方法

  1. func main() {
  2. router := gin.Default()
  3. router.GET("/get", func(c *gin.Context) {
  4. c.JSON(http.StatusOK, gin.H{"method": "get", "status": http.StatusOK})
  5. })
  6. router.POST("/post", func(c *gin.Context) {
  7. c.JSON(http.StatusCreated, gin.H{"method": "post", "status": http.StatusCreated})
  8. })
  9. router.PUT("/put", func(c *gin.Context) {
  10. c.JSON(http.StatusResetContent, gin.H{"method": "put", "status": http.StatusResetContent})
  11. })
  12. router.DELETE("/delete", func(c *gin.Context) {
  13. c.JSON(http.StatusNoContent, gin.H{"method": "delete", "status": http.StatusNoContent})
  14. })
  15. router.Run()
  16. }

访问路由使用相应的http方法就行

20. 文件上传

单文件

  1. func main() {
  2. router := gin.Default()
  3. router.POST("/upload", func(c *gin.Context) {
  4. // 单文件
  5. file, _ := c.FormFile("file")
  6. log.Println(file.Filename)
  7. // 上传文件至指定目录
  8. c.SaveUploadedFile(file, "demo_pic.png")
  9. c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
  10. })
  11. router.Run(":8080")
  12. }

多文件

  1. func main() {
  2. router := gin.Default()
  3. router.POST("/upload", func(c *gin.Context) {
  4. // Multipart form
  5. form, _ := c.MultipartForm()
  6. files := form.File["upload"]
  7. for _, file := range files {
  8. log.Println(file.Filename)
  9. // 上传文件至指定目录
  10. c.SaveUploadedFile(file, "./"+file.Filename)
  11. }
  12. c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
  13. })
  14. router.Run(":8080")
  15. }

21. 中间件中使用协程

  1. func main() {
  2. r := gin.Default()
  3. // r.GET("/long_async", func(c *gin.Context) {
  4. // // 创建在 goroutine 中使用的副本
  5. // cCp := c.Copy()
  6. // go func() {
  7. // // 用 time.Sleep() 模拟一个长任务。
  8. // time.Sleep(5 * time.Second)
  9. // // 请注意您使用的是复制的上下文 "cCp",这一点很重要
  10. // log.Println("Done! in path " + cCp.Request.URL.Path)
  11. // }()
  12. // })
  13. r.GET("/long_sync", func(c *gin.Context) {
  14. // 用 time.Sleep() 模拟一个长任务。
  15. time.Sleep(5 * time.Second)
  16. // 因为没有使用 goroutine,不需要拷贝上下文
  17. log.Println("Done! in path " + c.Request.URL.Path)
  18. })
  19. // 监听并在 0.0.0.0:8080 上启动服务
  20. r.Run(":8080")
  21. }

22. 自定义中间件

  1. func Logger() gin.HandlerFunc {
  2. return func(c *gin.Context) {
  3. t := time.Now()
  4. // 设置 example 变量
  5. c.Set("example", "12345")
  6. // 请求前
  7. c.Next()
  8. // 请求后
  9. latency := time.Since(t)
  10. log.Print(latency)
  11. // 获取发送的 status
  12. status := c.Writer.Status()
  13. log.Println(status)
  14. }
  15. }
  16. func main() {
  17. r := gin.New()
  18. r.Use(Logger())
  19. r.GET("/test", func(c *gin.Context) {
  20. example := c.MustGet("example").(string)
  21. // 打印:"12345"
  22. log.Println(example)
  23. })
  24. r.Run(":8080")
  25. }