示例
1. AsciiJSON
使用 AsciiJSON 对非Ascii码的字符转义为Unicode
func main() {r := gin.Default()r.GET("/someJSON", func(c *gin.Context) {data := map[string]interface{}{"lang语言": "GO语言","tag1212": "<br>",}// 在这里会对中文以及特殊符号处理 -- 处理为Unicode编码形式c.AsciiJSON(http.StatusOK, data)})r.Run(":8080")}
http返回结果为:
{"lang\u8bed\u8a00":"GO\u8bed\u8a00","tag1212":"\u003cbr\u003e"}
2. 绑定表单数据到自定义结构体
Bind form-data request with custom struct
数据结构
type StructA struct {FieldA string `form:"field_a"`}type StructB struct {NestedStruct StructAFieldB string `form:"field_b"`}type StructC struct {NestedStructPointer *StructAFieldC string `form:"field_c"`}type StructD struct {NestedAnonyStruct struct {FieldX string `form:"field_x"`}FieldD string `form:"field_d"`}func GetDataB(c *gin.Context) {var b StructBc.Bind(&b)c.JSON(200, gin.H{"a": b.NestedStruct,"b": b.FieldB,})}func GetDataC(c *gin.Context) {var b StructCc.Bind(&b)c.JSON(200, gin.H{"a": b.NestedStructPointer,"c": b.FieldC,})}func GetDataD(c *gin.Context) {var b StructDc.Bind(&b)c.JSON(200, gin.H{"x": b.NestedAnonyStruct,"d": b.FieldD,})}
示例1
http://localhost:8080/getb?field_a=hello&field_b=world
在getb中绑定的是结构体B (字段分别为结构体A(field_a)和field_b)
返回结果:
{"a":{"FieldA":"hello"},"b":"world"}
示例2
http://localhost:8080/getc?field_a=hello&field_c=world
在getb中绑定的是结构体C(字段分别为结构体A(field_a)和field_c)
返回结果:
{"a":{"FieldA":"hello"},"c":"world"}
示例3
http://localhost:8080/getd?field_x=hello&field_d=world
在getd中绑定的是结构体D(字段分别为新定义的结构体A(field_a)和field_d)
返回结果:
{"d":"world","x":{"FieldX":"hello"}}
同理只要把相应的方法改为Post 参数以form-data的形式在表单传入效果相同
3. 绑定查询字符串或表单数据
Bind query string or post data
数据结构
package mainimport ("log""time""github.com/gin-gonic/gin")// 目标结构体type Person struct {Name string `form:"name"`Address string `form:"address"`Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`}func main() {route := gin.Default()route.GET("/testingg", startPage)route.POST("/testingp", startPage)route.Run(":8080")}func startPage(c *gin.Context) {var person Personif c.ShouldBind(&person) == nil {log.Println(person.Name)log.Println(person.Address)log.Println(person.Birthday)}c.JSON(200, gin.H{"name": person.Name,"address": person.Address,"birthday": person.Birthday,})}
Get
直接在路由上加参数 如 http://localhost:8080/testingg?name=appleboy&address=xyz&birthday=1992-03-15
返回数据:
{"address":"xyz","birthday":"1992-03-15T00:00:00Z","name":"appleboy"}
需注意的是 参数名称必须和结构体字段的Tag一致
如路由为http://localhost:8080/testingg?Name=appleboy&address=xyz&birthday=1992-03-15
返回数据为:
{"address":"xyz","birthday":"1992-03-15T00:00:00Z","name":""}
4. 绑定URI
数据结构:
package mainimport "github.com/gin-gonic/gin"type Person struct {ID string `uri:"id" binding:"required,uuid"`Name string `uri:"name" binding:"required"`}func main() {route := gin.Default()route.GET("/:name/:id", func(c *gin.Context) {var person Personif err := c.ShouldBindUri(&person); err != nil {c.JSON(400, gin.H{"msg": err})return}c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})})route.Run(":8080")}
对参数进行格式校验,以及是否必须进行校验 ,只要是路径不匹配,绑定都会失败
5. 自定义Http配置
直接使用ListenAndServer
router := gin.Default()router.GET("/get", func(c *gin.Context) {c.JSON(200, "Success")})http.ListenAndServe(":8080", router)
或自定义进行配置
router := gin.Default()router.GET("/get", func(c *gin.Context) {c.JSON(200, "Success")})s := &http.Server{Addr: ":8080",Handler: router,ReadTimeout: 10 * time.Second,WriteTimeout: 10 * time.Second,MaxHeaderBytes: 1 << 20,}s.ListenAndServe()
6. Html渲染
数据直接返回到指定的模板文件
func main() {router := gin.Default()router.LoadHTMLGlob("./templates/*")router.GET("/index", func(c *gin.Context) {c.HTML(http.StatusOK, "index.tmpl", gin.H{"title": "Main website",})})router.Run(":8080")}
<html><h1>{{ .title }}</h1></html>
7. 记录日志
func main() {gin.DisableConsoleColor()f, _ := os.Create("gin.log")gin.DefaultWriter = io.MultiWriter(f)router := gin.Default()router.GET("/ping", func(c *gin.Context) {c.String(200, "pong")})router.Run(":8080")}
在请求时可以查看到指定路径的日志文件里面写入的日志
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.- using env: export GIN_MODE=release- using code: gin.SetMode(gin.ReleaseMode)[GIN-debug] GET /ping --> main.main.func1 (3 handlers)[GIN-debug] Listening and serving HTTP on :8080[GIN] xxxxxxx | 200 | 1.0036ms | ::1 | GET "/ping"[GIN] xxxxxxx | 200 | 0s | ::1 | GET "/ping"
8. XML/JSON/YAML/ProtoBuf 渲染
Json渲染
使用简单数据
func main() {r := gin.Default()// gin.H 是一个类型为 map[string]interface{}的数据格式r.GET("/json", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})})r.Run(":8080")}
返回数据为
{"message":"hey","status":200}
使用结构体
func main() {r := gin.Default()r.GET("/sjson", func(c *gin.Context) {// 使用结构体 并返回Jsonvar msg struct {Name string `json:"user"`Message stringNumber int}msg.Name = "Lena"msg.Message = "hey"msg.Number = 123// 最终返回的键都是已Tag为标准c.JSON(http.StatusOK, msg)})r.Run(":8080")}
返回结果为:
{"user":"Lena","Message":"hey","Number":123}
XML渲染
func main() {r := gin.Default()r.GET("/xml", func(c *gin.Context) {c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})})r.Run(":8080")}
返回结果为:
<map><message>hey</message><status>200</status></map>
Yaml
func main() {r := gin.Default()r.GET("/yaml", func(c *gin.Context) {c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})})r.Run(":8080")}
ProtoBuf
9. 中间件的使用
日志中间件使用示例
func main() {// 新建一个没有任何默认中间件的路由r := gin.New()// LLogger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。gin.DefaultWriter = os.Stdout // 控制台输出r.Use(gin.Logger())r.GET("/ping", func(c *gin.Context) {c.String(200, "pong")})r.Run(":8080")}
使用中间件后控制台可以看到输出情况
[GIN-debug] Listening and serving HTTP on :8080[GIN] xxxxx - 17:53:15 | 200 | 2.1258ms | ::1 | GET "/ping"[GIN] xxxxx - 17:53:16 | 200 | 0s | ::1 | GET "/ping"
对指定的路由使用指定的中间件
func main() {// 新建一个没有任何默认中间件的路由r := gin.New()// 对于特定的路由可以指定多个中间件r.GET("/benchmark",gin.Logger())r.GET("/ping",gin.Logger(),func(c *gin.Context) {c.String(200, "pong")})r.Run(":8080")}
使用 BasicAuth 中间件
// 简单的用户信息var secrets = gin.H{"foo": gin.H{"email": "foo@bar.com", "phone": "123433"},"austin": gin.H{"email": "austin@example.com", "phone": "666"},"lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},}func main() {r := gin.Default()// 路由组使用BasicAuth中间件authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{"foo": "bar","austin": "1234","lena": "hello2","manu": "4321",}))authorized.GET("/secrets", func(c *gin.Context) {user := c.MustGet(gin.AuthUserKey).(string)fmt.Println(user, "-------")if secret, ok := secrets[user]; ok {c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})} else {c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})}})r.Run(":8080")}
说明:
当请求头中没有Authorization或者是Authorization值无效时,需要登陆
10. 请求参数映射为Map
Map as querystring or postform parameters
func main() {router := gin.Default()router.POST("/post", func(c *gin.Context) {// 把在路由中ids的键作为map的键ids := c.QueryMap("ids") // 从url中传入names := c.PostFormMap("names") // 从form-data中传入fmt.Println("---------------")fmt.Printf("ids: %v; names: %v\n", ids, names)fmt.Println("---------------")})router.Run(":8080")}

输出结果为: ids: map[a:1234 b:hello]; names: map[first:thinkerou second:tianou]
简单来说该功能就是允许参数有不同的取值并且在后台可以映射为Map,方便其他操作
11. 获取字符串参数
Query string parameters
func main() {router := gin.Default()router.GET("/welcome", func(c *gin.Context) {firstname := c.DefaultQuery("firstname", "Guest") // 如果没有会有默认值lastname := c.Query("lastname") // 没有默认值c.String(http.StatusOK, "Hello %s %s", firstname, lastname)})router.Run(":8080")}
请求地址为 http://localhost:8080/welcome?firstname=454&lastname=89898
返回结果为:
Hello 454 89898
12. 路由组
Grouping routes
func main() {router := gin.Default()v1 := router.Group("/v1"){v1.POST("/login", func(c *gin.Context) {c.String(http.StatusOK, "Hello login1")})v1.POST("/submit", func(c *gin.Context) {c.String(http.StatusOK, "Hello submit1")})v1.POST("/read", func(c *gin.Context) {c.String(http.StatusOK, "Hello read1")})}v2 := router.Group("/v2"){v2.POST("/login", func(c *gin.Context) {c.String(http.StatusOK, "Hello login2")})v2.POST("/submit", func(c *gin.Context) {c.String(http.StatusOK, "Hello submit2")})v2.POST("/read", func(c *gin.Context) {c.String(http.StatusOK, "Hello read2")})}router.Run(":8080")}
这样组织路由,就可以对路由尽心分组/或者按版本进行管理了
http://localhost:8080/v2/read/http://localhost:8080/v2/read/
13. 路由参数
Parameters in path — 匹配路由中的参数(动态路由)
func main() {router := gin.Default()// /user/:name/ 只能匹配 /user/{parms} 或 /user/{param}/ 其他无法匹配// /user/:name 只能匹配 /user/{parms} 或 /user/{param}/ 其他无法匹配// router.GET("/user/:name", func(c *gin.Context) {// name := c.Param("name")// fmt.Println("name--", name)// c.String(http.StatusOK, "Hello %s", name)// })// 会匹配到 /user/{param}/ 但是不会匹配到/user/{param}router.GET("/user/:name/*action", func(c *gin.Context) {name := c.Param("name")action := c.Param("action")message := name + " is " + actionc.String(http.StatusOK, message)})router.Run(":8080")}
14. 重定向
Redirects
func main() {router := gin.Default()// 重定向外部路由router.GET("/test", func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")})// 重定向内部路由 访问test1 执行test2router.GET("/test1", func(c *gin.Context) {c.Request.URL.Path = "/test2"router.HandleContext(c)})router.GET("/test2", func(c *gin.Context) {c.JSON(200, gin.H{"hello": "world"})})router.Run(":8080")}
15. PureJSON
通常,JSON用它们的unicode实体替换特殊的HTML字符,例如<变成\u003c。如果需要原样输出,则可以使用PureJSON。此功能在Go 1.6及更低版本中不可用。
func main() {r := gin.Default()// 提供 unicode 实体r.GET("/json", func(c *gin.Context) {c.JSON(200, gin.H{"html": "<b>Hello, world!</b>",})})// 提供字面字符r.GET("/purejson", func(c *gin.Context) {c.PureJSON(200, gin.H{"html": "<b>Hello, world!</b>",})})// 监听并在 0.0.0.0:8080 上启动服务r.Run(":8080")}
访问json返回结果:
{"html":"\u003cb\u003eHello, world!\u003c/b\u003e"}
访问purejson返回结果:
{"html":"<b>Hello, world!</b>"}
16. SecureJSON
使用 SecureJSON 防止 json 劫持。 因为JSON数组默认为是可执行的JS。如果给定的结构是数组值,则默认预置 "while(1)," 到响应体。
func main() {r := gin.Default()// 你也可以使用自己的 SecureJSON 前缀r.SecureJsonPrefix("demo',\n")r.GET("/someJSON", func(c *gin.Context) {names := []string{"lena", "austin", "foo"}// 将输出:demo;["lena","austin","foo"]c.SecureJSON(http.StatusOK, names)})r.Run(":8080")}
返回数据:
demo',["lena","austin","foo"]
17. JSONP
使用 JSONP 向不同域的服务器请求数据。如果查询参数存在回调,则将回调添加到响应体中(达到跨域的目)
func main() {r := gin.Default()r.GET("/jsonp", func(c *gin.Context) {data := map[string]interface{}{"foo": "bar",}// callback 是 xc.JSONP(http.StatusOK, data)})r.Run(":8080")}
返回结果为:
x({"foo":"bar"});// 服务端返回对应的JS类型的字符串,被浏览器执行,达到跨域的目的。
18. 从Reader中保存数据
Serving data from reader
func main() {router := gin.Default()router.GET("/someDataFromReader", func(c *gin.Context) {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")if err != nil || response.StatusCode != http.StatusOK {c.Status(http.StatusServiceUnavailable)return}reader := response.BodycontentLength := response.ContentLengthcontentType := response.Header.Get("Content-Type")extraHeaders := map[string]string{"Content-Disposition": `attachment; filename="gopher.png"`,}fmt.Println("contentLength----", contentLength, "contentType---", contentType, "reader---", reader, "extraHeaders----", extraHeaders)c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)})router.Run(":8080")}
19. 使用 HTTP 方法
func main() {router := gin.Default()router.GET("/get", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"method": "get", "status": http.StatusOK})})router.POST("/post", func(c *gin.Context) {c.JSON(http.StatusCreated, gin.H{"method": "post", "status": http.StatusCreated})})router.PUT("/put", func(c *gin.Context) {c.JSON(http.StatusResetContent, gin.H{"method": "put", "status": http.StatusResetContent})})router.DELETE("/delete", func(c *gin.Context) {c.JSON(http.StatusNoContent, gin.H{"method": "delete", "status": http.StatusNoContent})})router.Run()}
20. 文件上传
单文件
func main() {router := gin.Default()router.POST("/upload", func(c *gin.Context) {// 单文件file, _ := c.FormFile("file")log.Println(file.Filename)// 上传文件至指定目录c.SaveUploadedFile(file, "demo_pic.png")c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))})router.Run(":8080")}
多文件
func main() {router := gin.Default()router.POST("/upload", func(c *gin.Context) {// Multipart formform, _ := c.MultipartForm()files := form.File["upload"]for _, file := range files {log.Println(file.Filename)// 上传文件至指定目录c.SaveUploadedFile(file, "./"+file.Filename)}c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))})router.Run(":8080")}
21. 中间件中使用协程
func main() {r := gin.Default()// r.GET("/long_async", func(c *gin.Context) {// // 创建在 goroutine 中使用的副本// cCp := c.Copy()// go func() {// // 用 time.Sleep() 模拟一个长任务。// time.Sleep(5 * time.Second)// // 请注意您使用的是复制的上下文 "cCp",这一点很重要// log.Println("Done! in path " + cCp.Request.URL.Path)// }()// })r.GET("/long_sync", func(c *gin.Context) {// 用 time.Sleep() 模拟一个长任务。time.Sleep(5 * time.Second)// 因为没有使用 goroutine,不需要拷贝上下文log.Println("Done! in path " + c.Request.URL.Path)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(":8080")}
22. 自定义中间件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()// 设置 example 变量c.Set("example", "12345")// 请求前c.Next()// 请求后latency := time.Since(t)log.Print(latency)// 获取发送的 statusstatus := c.Writer.Status()log.Println(status)}}func main() {r := gin.New()r.Use(Logger())r.GET("/test", func(c *gin.Context) {example := c.MustGet("example").(string)// 打印:"12345"log.Println(example)})r.Run(":8080")}
