整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          gin中文文檔示例

          gin中文文檔示例



          該節列出了 api 的用法。

          AsciiJSON

          使用 AsciiJSON 生成具有轉義的非 ASCII 字符的 ASCII-only JSON。

          func main() {
              r :=gin.Default()
          
              r.GET("/someJSON", func(c *gin.Context) {
                  data :=map[string]interface{}{
                      "lang": "GO語言",
                      "tag":  "<br>",
                  }
          
                  // 輸出 : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
                  c.AsciiJSON(http.StatusOK, data)
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run(":8080")
          }
          

          HTML 渲染

          使用 LoadHTMLGlob() 或者 LoadHTMLFiles()

          func main() {
              router :=gin.Default()
              router.LoadHTMLGlob("templates/*")
              //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
              router.GET("/index", func(c *gin.Context) {
                  c.HTML(http.StatusOK, "index.tmpl", gin.H{
                      "title": "Main website",
                  })
              })
              router.Run(":8080")
          }
          

          templates/index.tmpl

          <html>
              <h1>
                  {{ .title }}
              </h1>
          </html>
          

          使用不同目錄下名稱相同的模板

          func main() {
              router :=gin.Default()
              router.LoadHTMLGlob("templates/**/*")
              router.GET("/posts/index", func(c *gin.Context) {
                  c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
                      "title": "Posts",
                  })
              })
              router.GET("/users/index", func(c *gin.Context) {
                  c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
                      "title": "Users",
                  })
              })
              router.Run(":8080")
          }
          

          templates/posts/index.tmpl

          {{ define "posts/index.tmpl" }}
          <html><h1>
              {{ .title }}
          </h1>
          <p>Using posts/index.tmpl</p>
          </html>
          {{ end }}
          

          templates/users/index.tmpl

          {{ define "users/index.tmpl" }}
          <html><h1>
              {{ .title }}
          </h1>
          <p>Using users/index.tmpl</p>
          </html>
          {{ end }}
          

          自定義模板渲染器

          你可以使用自定義的 html 模板渲染

          import "html/template"
          
          func main() {
              router :=gin.Default()
              html :=template.Must(template.ParseFiles("file1", "file2"))
              router.SetHTMLTemplate(html)
              router.Run(":8080")
          }
          

          自定義分隔符

          你可以使用自定義分隔

              r :=gin.Default()
              r.Delims("{[{", "}]}")
              r.LoadHTMLGlob("/path/to/templates")
          

          自定義模板功能

          查看詳細示例代碼。

          main.go

          import (
              "fmt"
              "html/template"
              "net/http"
              "time"
          
              "github.com/gin-gonic/gin"
          )
          
          func formatAsDate(t time.Time) string {
              year, month, day :=t.Date()
              return fmt.Sprintf("%d/%02d/%02d", year, month, day)
          }
          
          func main() {
              router :=gin.Default()
              router.Delims("{[{", "}]}")
              router.SetFuncMap(template.FuncMap{
                  "formatAsDate": formatAsDate,
              })
              router.LoadHTMLFiles("./testdata/template/raw.tmpl")
          
              router.GET("/raw", func(c *gin.Context) {
                  c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
                      "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
                  })
              })
          
              router.Run(":8080")
          }
          

          raw.tmpl

          Date: {[{.now | formatAsDate}]}
          

          結果:

          Date: 2017/07/01
          

          HTTP2 server 推送

          http.Pusher 僅支持 go1.8+。 更多信息,請查閱 golang blog。

          package main
          
          import (
              "html/template"
              "log"
          
              "github.com/gin-gonic/gin"
          )
          
          var html=template.Must(template.New("https").Parse(`
          <html>
          <head>
            <title>Https Test</title>
            <script src="/assets/app.js"></script>
          </head>
          <body>
            <h1 style="color:red;">Welcome, Ginner!</h1>
          </body>
          </html>
          `))
          
          func main() {
              r :=gin.Default()
              r.Static("/assets", "./assets")
              r.SetHTMLTemplate(html)
          
              r.GET("/", func(c *gin.Context) {
                  if pusher :=c.Writer.Pusher(); pusher !=nil {
                      // 使用 pusher.Push() 做服務器推送
                      if err :=pusher.Push("/assets/app.js", nil); err !=nil {
                          log.Printf("Failed to push: %v", err)
                      }
                  }
                  c.HTML(200, "https", gin.H{
                      "status": "success",
                  })
              })
          
              // 監聽并在 https://127.0.0.1:8080 上啟動服務
              r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
          }
          

          JSONP

          使用 JSONP 向不同域的服務器請求數據。如果查詢參數存在回調,則將回調添加到響應體中。

          func main() {
              r :=gin.Default()
          
              r.GET("/JSONP", func(c *gin.Context) {
                  data :=map[string]interface{}{
                      "foo": "bar",
                  }
                  
                  // /JSONP?callback=x
                  // 將輸出:x({\"foo\":\"bar\"})
                  c.JSONP(http.StatusOK, data)
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run(":8080")
          }
          

          Multipart/Urlencoded 綁定

          package main
          
          import (
              "github.com/gin-gonic/gin"
          )
          
          type LoginForm struct {
              User     string `form:"user" binding:"required"`
              Password string `form:"password" binding:"required"`
          }
          
          func main() {
              router :=gin.Default()
              router.POST("/login", func(c *gin.Context) {
                  // 你可以使用顯式綁定聲明綁定 multipart form:
                  // c.ShouldBindWith(&form, binding.Form)
                  // 或者簡單地使用 ShouldBind 方法自動綁定:
                  var form LoginForm
                  // 在這種情況下,將自動選擇合適的綁定
                  if c.ShouldBind(&form)==nil {
                      if form.User=="user" && form.Password=="password" {
                          c.JSON(200, gin.H{"status": "you are logged in"})
                      } else {
                          c.JSON(401, gin.H{"status": "unauthorized"})
                      }
                  }
              })
              router.Run(":8080")
          }
          

          測試:

          $ curl -v --form user=user --form password=password http://localhost:8080/login
          

          Multipart/Urlencoded 表單

          func main() {
              router :=gin.Default()
          
              router.POST("/form_post", func(c *gin.Context) {
                  message :=c.PostForm("message")
                  nick :=c.DefaultPostForm("nick", "anonymous")
          
                  c.JSON(200, gin.H{
                      "status":  "posted",
                      "message": message,
                      "nick":    nick,
                  })
              })
              router.Run(":8080")
          }
          

          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")
          }
          

          Query 和 post form

          POST /post?id=1234&page=1 HTTP/1.1
          Content-Type: application/x-www-form-urlencoded
          
          name=manu&message=this_is_great
          func main() {
              router :=gin.Default()
          
              router.POST("/post", func(c *gin.Context) {
          
                  id :=c.Query("id")
                  page :=c.DefaultQuery("page", "0")
                  name :=c.PostForm("name")
                  message :=c.PostForm("message")
          
                  fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
              })
              router.Run(":8080")
          }
          id: 1234; page: 1; name: manu; message: this_is_great
          

          SecureJSON

          使用 SecureJSON 防止 json 劫持。如果給定的結構是數組值,則默認預置 "while(1)," 到響應體。

          func main() {
              r :=gin.Default()
          
              // 你也可以使用自己的 SecureJSON 前綴
              // r.SecureJsonPrefix(")]}',\n")
          
              r.GET("/someJSON", func(c *gin.Context) {
                  names :=[]string{"lena", "austin", "foo"}
          
                  // 將輸出:while(1);["lena","austin","foo"]
                  c.SecureJSON(http.StatusOK, names)
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run(":8080")
          }
          

          XML/JSON/YAML/ProtoBuf 渲染

          func main() {
              r :=gin.Default()
          
              // gin.H 是 map[string]interface{} 的一種快捷方式
              r.GET("/someJSON", func(c *gin.Context) {
                  c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
              })
          
              r.GET("/moreJSON", func(c *gin.Context) {
                  // 你也可以使用一個結構體
                  var msg struct {
                      Name    string `json:"user"`
                      Message string
                      Number  int
                  }
                  msg.Name="Lena"
                  msg.Message="hey"
                  msg.Number=123
                  // 注意 msg.Name 在 JSON 中變成了 "user"
                  // 將輸出:{"user": "Lena", "Message": "hey", "Number": 123}
                  c.JSON(http.StatusOK, msg)
              })
          
              r.GET("/someXML", func(c *gin.Context) {
                  c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
              })
          
              r.GET("/someYAML", func(c *gin.Context) {
                  c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
              })
          
              r.GET("/someProtoBuf", func(c *gin.Context) {
                  reps :=[]int64{int64(1), int64(2)}
                  label :="test"
                  // protobuf 的具體定義寫在 testdata/protoexample 文件中。
                  data :=&protoexample.Test{
                      Label: &label,
                      Reps:  reps,
                  }
                  // 請注意,數據在響應中變為二進制數據
                  // 將輸出被 protoexample.Test protobuf 序列化了的數據
                  c.ProtoBuf(http.StatusOK, data)
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run(":8080")
          }
          

          上傳文件

          本節列出了上傳圖片的 api 用法。

          單文件

          參考 issue #774 和詳細示例代碼.

          func main() {
              router :=gin.Default()
              // 為 multipart forms 設置較低的內存限制 (默認是 32 MiB)
              router.MaxMultipartMemory=8 << 20  // 8 MiB
              router.POST("/upload", func(c *gin.Context) {
                  // 單文件
                  file, _ :=c.FormFile("file")
                  log.Println(file.Filename)
          
                  // 上傳文件至指定目錄
                  c.SaveUploadedFile(file, dst)
          
                  c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
              })
              router.Run(":8080")
          }
          

          如何使用 curl:

          curl -X POST http://localhost:8080/upload \
            -F "file=@/Users/appleboy/test.zip" \
            -H "Content-Type: multipart/form-data"
          

          多文件

          查看詳細示例代碼.

          func main() {
              router :=gin.Default()
              // 為 multipart forms 設置較低的內存限制 (默認是 32 MiB)
              router.MaxMultipartMemory=8 << 20  // 8 MiB
              router.POST("/upload", func(c *gin.Context) {
                  // Multipart form
                  form, _ :=c.MultipartForm()
                  files :=form.File["upload[]"]
          
                  for _, file :=range files {
                      log.Println(file.Filename)
          
                      // 上傳文件至指定目錄
                      c.SaveUploadedFile(file, dst)
                  }
                  c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
              })
              router.Run(":8080")
          }
          

          如何使用 curl:

          curl -X POST http://localhost:8080/upload \
            -F "upload[]=@/Users/appleboy/test1.zip" \
            -F "upload[]=@/Users/appleboy/test2.zip" \
            -H "Content-Type: multipart/form-data"
          

          不使用默認的中間件

          使用

          r :=gin.New()
          

          代替

          // Default 使用 Logger 和 Recovery 中間件
          r :=gin.Default()
          

          從 reader 讀取數據

          func main() {
              router :=gin.Default()
              router.GET("/someDataFromReader", func(c *gin.Context) {
                  response, err :=http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")
                  if err !=nil || response.StatusCode !=http.StatusOK {
                      c.Status(http.StatusServiceUnavailable)
                      return
                  }
          
                  reader :=response.Body
                  contentLength :=response.ContentLength
                  contentType :=response.Header.Get("Content-Type")
          
                  extraHeaders :=map[string]string{
                      "Content-Disposition": `attachment; filename="gopher.png"`,
                  }
          
                  c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
              })
              router.Run(":8080")
          }
          

          優雅地重啟或停止

          你想優雅地重啟或停止 web 服務器嗎?有一些方法可以做到這一點。

          我們可以使用 fvbock/endless 來替換默認的 ListenAndServe。更多詳細信息,請參閱 issue #296。

          router :=gin.Default()
          router.GET("/", handler)
          // [...]
          endless.ListenAndServe(":4242", router)
          

          替代方案:

          • manners:可以優雅關機的 Go Http 服務器。
          • graceful:Graceful 是一個 Go 擴展包,可以優雅地關閉 http.Handler 服務器。
          • grace:Go 服務器平滑重啟和零停機時間部署。

          如果你使用的是 Go 1.8,可以不需要這些庫!考慮使用 http.Server 內置的 Shutdown() 方法優雅地關機. 請參閱 gin 完整的 graceful-shutdown 示例。

          // +build go1.8
          
          package main
          
          import (
              "context"
              "log"
              "net/http"
              "os"
              "os/signal"
              "time"
          
              "github.com/gin-gonic/gin"
          )
          
          func main() {
              router :=gin.Default()
              router.GET("/", func(c *gin.Context) {
                  time.Sleep(5 * time.Second)
                  c.String(http.StatusOK, "Welcome Gin Server")
              })
          
              srv :=&http.Server{
                  Addr:    ":8080",
                  Handler: router,
              }
          
              go func() {
                  // 服務連接
                  if err :=srv.ListenAndServe(); err !=nil && err !=http.ErrServerClosed {
                      log.Fatalf("listen: %s\n", err)
                  }
              }()
          
              // 等待中斷信號以優雅地關閉服務器(設置 5 秒的超時時間)
              quit :=make(chan os.Signal)
              signal.Notify(quit, os.Interrupt)
              <-quit
              log.Println("Shutdown Server ...")
          
              ctx, cancel :=context.WithTimeout(context.Background(), 5*time.Second)
              defer cancel()
              if err :=srv.Shutdown(ctx); err !=nil {
                  log.Fatal("Server Shutdown:", err)
              }
              log.Println("Server exiting")
          }
          

          使用 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()
          
              // 路由組使用 gin.BasicAuth() 中間件
              // gin.Accounts 是 map[string]string 的一種快捷方式
              authorized :=r.Group("/admin", gin.BasicAuth(gin.Accounts{
                  "foo":    "bar",
                  "austin": "1234",
                  "lena":   "hello2",
                  "manu":   "4321",
              }))
          
              // /admin/secrets 端點
              // 觸發 "localhost:8080/admin/secrets
              authorized.GET("/secrets", func(c *gin.Context) {
                  // 獲取用戶,它是由 BasicAuth 中間件設置的
                  user :=c.MustGet(gin.AuthUserKey).(string)
                  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 :("})
                  }
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run(":8080")
          }
          

          使用 HTTP 方法

          func main() {
              // 禁用控制臺顏色
              // gin.DisableConsoleColor()
          
              // 使用默認中間件(logger 和 recovery 中間件)創建 gin 路由
              router :=gin.Default()
          
              router.GET("/someGet", getting)
              router.POST("/somePost", posting)
              router.PUT("/somePut", putting)
              router.DELETE("/someDelete", deleting)
              router.PATCH("/somePatch", patching)
              router.HEAD("/someHead", head)
              router.OPTIONS("/someOptions", options)
          
              // 默認在 8080 端口啟動服務,除非定義了一個 PORT 的環境變量。
              router.Run()
              // router.Run(":3000") hardcode 端口號
          }
          

          使用中間件

          func main() {
              // 新建一個沒有任何默認中間件的路由
              r :=gin.New()
          
              // 全局中間件
              // Logger 中間件將日志寫入 gin.DefaultWriter,即使你將 GIN_MODE 設置為 release。
              // By default gin.DefaultWriter=os.Stdout
              r.Use(gin.Logger())
          
              // Recovery 中間件會 recover 任何 panic。如果有 panic 的話,會寫入 500。
              r.Use(gin.Recovery())
          
              // 你可以為每個路由添加任意數量的中間件。
              r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
          
              // 認證路由組
              // authorized :=r.Group("/", AuthRequired())
              // 和使用以下兩行代碼的效果完全一樣:
              authorized :=r.Group("/")
              // 路由組中間件! 在此例中,我們在 "authorized" 路由組中使用自定義創建的 
              // AuthRequired() 中間件
              authorized.Use(AuthRequired())
              {
                  authorized.POST("/login", loginEndpoint)
                  authorized.POST("/submit", submitEndpoint)
                  authorized.POST("/read", readEndpoint)
          
                  // 嵌套路由組
                  testing :=authorized.Group("testing")
                  testing.GET("/analytics", analyticsEndpoint)
              }
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run(":8080")
          }
          

          只綁定 url 查詢字符串

          ShouldBindQuery 函數只綁定 url 查詢參數而忽略 post 數據。參閱詳細信息.

          package main
          
          import (
              "log"
          
              "github.com/gin-gonic/gin"
          )
          
          type Person struct {
              Name    string `form:"name"`
              Address string `form:"address"`
          }
          
          func main() {
              route :=gin.Default()
              route.Any("/testing", startPage)
              route.Run(":8085")
          }
          
          func startPage(c *gin.Context) {
              var person Person
              if c.ShouldBindQuery(&person)==nil {
                  log.Println("======Only Bind By Query String======")
                  log.Println(person.Name)
                  log.Println(person.Address)
              }
              c.String(200, "Success")
          }
          

          在中間件中使用 Goroutine

          當在中間件或 handler 中啟動新的 Goroutine 時,不能使用原始的上下文,必須使用只讀副本。

          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")
          }
          

          多模板

          Gin 默認允許只使用一個 html 模板。 查看多模板渲染 以使用 go 1.6 block template 等功能。

          如何記錄日志

          func main() {
              // 禁用控制臺顏色,將日志寫入文件時不需要控制臺顏色。
              gin.DisableConsoleColor()
          
              // 記錄到文件。
              f, _ :=os.Create("gin.log")
              gin.DefaultWriter=io.MultiWriter(f)
          
              // 如果需要同時將日志寫入文件和控制臺,請使用以下代碼。
              // gin.DefaultWriter=io.MultiWriter(f, os.Stdout)
          
              router :=gin.Default()
              router.GET("/ping", func(c *gin.Context) {
                  c.String(200, "pong")
              })
          
              router.Run(":8080")
          }
          

          定義路由日志的格式

          默認的路由日志格式:

          [GIN-debug] POST   /foo                      --> main.main.func1 (3 handlers)
          [GIN-debug] GET    /bar                      --> main.main.func2 (3 handlers)
          [GIN-debug] GET    /status                   --> main.main.func3 (3 handlers)
          

          如果你想要以指定的格式(例如 JSON,key values 或其他格式)記錄信息,則可以使用 gin.DebugPrintRouteFunc 指定格式。 在下面的示例中,我們使用標準日志包記錄所有路由,但你可以使用其他滿足你需求的日志工具。

          import (
              "log"
              "net/http"
          
              "github.com/gin-gonic/gin"
          )
          
          func main() {
              r :=gin.Default()
              gin.DebugPrintRouteFunc=func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
                  log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
              }
          
              r.POST("/foo", func(c *gin.Context) {
                  c.JSON(http.StatusOK, "foo")
              })
          
              r.GET("/bar", func(c *gin.Context) {
                  c.JSON(http.StatusOK, "bar")
              })
          
              r.GET("/status", func(c *gin.Context) {
                  c.JSON(http.StatusOK, "ok")
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run()
          }
          

          將 request body 綁定到不同的結構體中

          一般通過調用 c.Request.Body 方法綁定數據,但不能多次調用這個方法。

          type formA struct {
            Foo string `json:"foo" xml:"foo" binding:"required"`
          }
          
          type formB struct {
            Bar string `json:"bar" xml:"bar" binding:"required"`
          }
          
          func SomeHandler(c *gin.Context) {
            objA :=formA{}
            objB :=formB{}
            // c.ShouldBind 使用了 c.Request.Body,不可重用。
            if errA :=c.ShouldBind(&objA); errA==nil {
              c.String(http.StatusOK, `the body should be formA`)
            // 因為現在 c.Request.Body 是 EOF,所以這里會報錯。
            } else if errB :=c.ShouldBind(&objB); errB==nil {
              c.String(http.StatusOK, `the body should be formB`)
            } else {
              ...
            }
          }
          

          要想多次綁定,可以使用 c.ShouldBindBodyWith.

          func SomeHandler(c *gin.Context) {
            objA :=formA{}
            objB :=formB{}
            // 讀取 c.Request.Body 并將結果存入上下文。
            if errA :=c.ShouldBindBodyWith(&objA, binding.JSON); errA==nil {
              c.String(http.StatusOK, `the body should be formA`)
            // 這時, 復用存儲在上下文中的 body。
            } else if errB :=c.ShouldBindBodyWith(&objB, binding.JSON); errB==nil {
              c.String(http.StatusOK, `the body should be formB JSON`)
            // 可以接受其他格式
            } else if errB2 :=c.ShouldBindBodyWith(&objB, binding.XML); errB2==nil {
              c.String(http.StatusOK, `the body should be formB XML`)
            } else {
              ...
            }
          }
          
          • c.ShouldBindBodyWith 會在綁定之前將 body 存儲到上下文中。 這會對性能造成輕微影響,如果調用一次就能完成綁定的話,那就不要用這個方法。
          • 只有某些格式需要此功能,如 JSON, XML, MsgPack, ProtoBuf。 對于其他格式, 如 Query, Form, FormPost, FormMultipart 可以多次調用 c.ShouldBind() 而不會造成任任何性能損失 (詳見 #1341)。

          控制日志輸出顏色

          根據檢測到的 TTY,控制臺的日志輸出默認是有顏色的。

          禁止日志顏色化:

          func main() {
              // 禁止日志的顏色
              gin.DisableConsoleColor()
          
              // 用默認中間件創建一個 gin 路由:
              // 日志和恢復(無崩潰)中間件
              router :=gin.Default()
          
              router.GET("/ping", func(c *gin.Context) {
                  c.String(200, "pong")
              })
          
              router.Run(":8080")
          }
          

          日志顏色化:

          func main() {
              // 強制日志顏色化
              gin.ForceConsoleColor()
          
              // 用默認中間件創建一個 gin 路由:
              // 日志和恢復(無崩潰)中間件
              router :=gin.Default()
          
              router.GET("/ping", func(c *gin.Context) {
                  c.String(200, "pong")
              })
          
              router.Run(":8080")
          }
          

          支持 Let's Encrypt

          一行代碼支持 LetsEncrypt HTTPS servers 示例。

          package main
          
          import (
              "log"
          
              "github.com/gin-gonic/autotls"
              "github.com/gin-gonic/gin"
          )
          
          func main() {
              r :=gin.Default()
          
              // Ping handler
              r.GET("/ping", func(c *gin.Context) {
                  c.String(200, "pong")
              })
          
              log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
          }
          

          自定義 autocert manager 示例。

          package main
          
          import (
              "log"
          
              "github.com/gin-gonic/autotls"
              "github.com/gin-gonic/gin"
              "golang.org/x/crypto/acme/autocert"
          )
          
          func main() {
              r :=gin.Default()
          
              // Ping handler
              r.GET("/ping", func(c *gin.Context) {
                  c.String(200, "pong")
              })
          
              m :=autocert.Manager{
                  Prompt:     autocert.AcceptTOS,
                  HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
                  Cache:      autocert.DirCache("/var/www/.cache"),
              }
          
              log.Fatal(autotls.RunWithManager(r, &m))
          }
          

          映射查詢字符串或表單參數

          POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
          Content-Type: application/x-www-form-urlencoded
          
          names[first]=thinkerou&names[second]=tianou
          func main() {
              router :=gin.Default()
          
              router.POST("/post", func(c *gin.Context) {
          
                  ids :=c.QueryMap("ids")
                  names :=c.PostFormMap("names")
          
                  fmt.Printf("ids: %v; names: %v", ids, names)
              })
              router.Run(":8080")
          }
          ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]
          

          查詢字符串參數

          func main() {
              router :=gin.Default()
          
              // 使用現有的基礎請求對象解析查詢字符串參數。
              // 示例 URL: /welcome?firstname=Jane&lastname=Doe
              router.GET("/welcome", func(c *gin.Context) {
                  firstname :=c.DefaultQuery("firstname", "Guest")
                  lastname :=c.Query("lastname") // c.Request.URL.Query().Get("lastname") 的一種快捷方式
          
                  c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
              })
              router.Run(":8080")
          }
          

          模型綁定和驗證

          要將請求體綁定到結構體中,使用模型綁定。 Gin目前支持JSON、XML、YAML和標準表單值的綁定(foo=bar&boo=baz)。

          Gin使用 go-playground/validator.v8 進行驗證。 查看標簽用法的全部文檔.

          使用時,需要在要綁定的所有字段上,設置相應的tag。 例如,使用 JSON 綁定時,設置字段標簽為 json:"fieldname"。

          Gin提供了兩類綁定方法:

          • Type

          - Must bind

          • Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
          • Behavior - 這些方法屬于 MustBindWith 的具體調用。 如果發生綁定錯誤,則請求終止,并觸發 c.AbortWithError(400, err).SetType(ErrorTypeBind)。響應狀態碼被設置為 400 并且 Content-Type 被設置為 text/plain; charset=utf-8。 如果您在此之后嘗試設置響應狀態碼,Gin會輸出日志 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422。 如果您希望更好地控制綁定,考慮使用 ShouldBind 等效方法。
          • Type

          - Should bind

          • Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
          • Behavior - 這些方法屬于 ShouldBindWith 的具體調用。 如果發生綁定錯誤,Gin 會返回錯誤并由開發者處理錯誤和請求。

          使用 Bind 方法時,Gin 會嘗試根據 Content-Type 推斷如何綁定。 如果你明確知道要綁定什么,可以使用 MustBindWith 或 ShouldBindWith。

          你也可以指定必須綁定的字段。 如果一個字段的 tag 加上了 binding:"required",但綁定時是空值, Gin 會報錯。

          // 綁定 JSON
          type Login struct {
              User     string `form:"user" json:"user" xml:"user"  binding:"required"`
              Password string `form:"password" json:"password" xml:"password" binding:"required"`
          }
          
          func main() {
              router :=gin.Default()
          
              // 綁定 JSON ({"user": "manu", "password": "123"})
              router.POST("/loginJSON", func(c *gin.Context) {
                  var json Login
                  if err :=c.ShouldBindJSON(&json); err !=nil {
                      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
                      return
                  }
                  
                  if json.User !="manu" || json.Password !="123" {
                      c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
                      return
                  } 
                  
                  c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
              })
          
              // 綁定 XML (
              //  <?xml version="1.0" encoding="UTF-8"?>
              //  <root>
              //      <user>user</user>
              //      <password>123</password>
              //  </root>)
              router.POST("/loginXML", func(c *gin.Context) {
                  var xml Login
                  if err :=c.ShouldBindXML(&xml); err !=nil {
                      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
                      return
                  }
                  
                  if xml.User !="manu" || xml.Password !="123" {
                      c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
                      return
                  } 
                  
                  c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
              })
          
              // 綁定 HTML 表單 (user=manu&password=123)
              router.POST("/loginForm", func(c *gin.Context) {
                  var form Login
                  // 根據 Content-Type Header 推斷使用哪個綁定器。
                  if err :=c.ShouldBind(&form); err !=nil {
                      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
                      return
                  }
                  
                  if form.User !="manu" || form.Password !="123" {
                      c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
                      return
                  } 
                  
                  c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              router.Run(":8080")
          }
          

          示例請求

          $ curl -v -X POST \
            http://localhost:8080/loginJSON \
            -H 'content-type: application/json' \
            -d '{ "user": "manu" }'
          > POST /loginJSON HTTP/1.1
          > Host: localhost:8080
          > User-Agent: curl/7.51.0
          > Accept: */*
          > content-type: application/json
          > Content-Length: 18
          >
          * upload completely sent off: 18 out of 18 bytes
          < HTTP/1.1 400 Bad Request
          < Content-Type: application/json; charset=utf-8
          < Date: Fri, 04 Aug 2017 03:51:31 GMT
          < Content-Length: 100
          <
          {"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}
          

          忽略驗證

          使用上述的 curl 命令運行上面的示例時會返回錯誤。 因為示例中 Password 使用了 binding:"required"。 如果 Password 使用 binding:"-", 再次運行上面的示例就不會返回錯誤。

          綁定 HTML 復選框

          參見詳細信息

          main.go

          ...
          
          type myForm struct {
              Colors []string `form:"colors[]"`
          }
          
          ...
          
          func formHandler(c *gin.Context) {
              var fakeForm myForm
              c.ShouldBind(&fakeForm)
              c.JSON(200, gin.H{"color": fakeForm.Colors})
          }
          
          ...
          

          form.html

          <form action="/" method="POST">
              <p>Check some colors</p>
              <label for="red">Red</label>
              <input type="checkbox" name="colors[]" value="red" id="red">
              <label for="green">Green</label>
              <input type="checkbox" name="colors[]" value="green" id="green">
              <label for="blue">Blue</label>
              <input type="checkbox" name="colors[]" value="blue" id="blue">
              <input type="submit">
          </form>
          

          結果:

          {"color":["red","green","blue"]}
          

          綁定 Uri

          查看詳細信息.

          package main
          
          import "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 Person
                  if 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(":8088")
          }
          

          測試:

          $ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
          $ curl -v localhost:8088/thinkerou/not-uuid
          

          綁定查詢字符串或表單數據

          查看詳細信息。

          package main
          
          import (
              "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("/testing", startPage)
              route.Run(":8085")
          }
          
          func startPage(c *gin.Context) {
              var person Person
              // 如果是 `GET` 請求,只使用 `Form` 綁定引擎(`query`)。
              // 如果是 `POST` 請求,首先檢查 `content-type` 是否為 `JSON` 或 `XML`,然后再使用 `Form`(`form-data`)。
              // 查看更多:https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
              if c.ShouldBind(&person)==nil {
                  log.Println(person.Name)
                  log.Println(person.Address)
                  log.Println(person.Birthday)
              }
          
              c.String(200, "Success")
          }
          

          測試:

          $ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15"
          

          綁定表單數據至自定義結構體

          以下示例使用自定義結構體:

          type StructA struct {
              FieldA string `form:"field_a"`
          }
          
          type StructB struct {
              NestedStruct StructA
              FieldB string `form:"field_b"`
          }
          
          type StructC struct {
              NestedStructPointer *StructA
              FieldC 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 StructB
              c.Bind(&b)
              c.JSON(200, gin.H{
                  "a": b.NestedStruct,
                  "b": b.FieldB,
              })
          }
          
          func GetDataC(c *gin.Context) {
              var b StructC
              c.Bind(&b)
              c.JSON(200, gin.H{
                  "a": b.NestedStructPointer,
                  "c": b.FieldC,
              })
          }
          
          func GetDataD(c *gin.Context) {
              var b StructD
              c.Bind(&b)
              c.JSON(200, gin.H{
                  "x": b.NestedAnonyStruct,
                  "d": b.FieldD,
              })
          }
          
          func main() {
              r :=gin.Default()
              r.GET("/getb", GetDataB)
              r.GET("/getc", GetDataC)
              r.GET("/getd", GetDataD)
          
              r.Run()
          }
          

          使用 curl 命令結果:

          $ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
          {"a":{"FieldA":"hello"},"b":"world"}
          $ curl "http://localhost:8080/getc?field_a=hello&field_c=world"
          {"a":{"FieldA":"hello"},"c":"world"}
          $ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
          {"d":"world","x":{"FieldX":"hello"}}
          

          注意:不支持以下格式結構體:

          type StructX struct {
              X struct {} `form:"name_x"` // 有 form
          }
          
          type StructY struct {
              Y StructX `form:"name_y"` // 有 form
          }
          
          type StructZ struct {
              Z *StructZ `form:"name_z"` // 有 form
          }
          

          總之, 目前僅支持沒有 form 的嵌套結構體。

          自定義 HTTP 配置

          直接使用 http.ListenAndServe(),如下所示:

          func main() {
              router :=gin.Default()
              http.ListenAndServe(":8080", router)
          }
          

          func main() {
              router :=gin.Default()
          
              s :=&http.Server{
                  Addr:           ":8080",
                  Handler:        router,
                  ReadTimeout:    10 * time.Second,
                  WriteTimeout:   10 * time.Second,
                  MaxHeaderBytes: 1 << 20,
              }
              s.ListenAndServe()
          }
          

          自定義中間件

          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)
          
                  // 獲取發送的 status
                  status :=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)
              })
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              r.Run(":8080")
          }
          

          自定義驗證器

          注冊自定義驗證器,查看示例代碼.

          package main
          
          import (
              "net/http"
              "reflect"
              "time"
          
              "github.com/gin-gonic/gin"
              "github.com/gin-gonic/gin/binding"
              "github.com/go-playground/validator/v10"
          )
          
          // Booking 包含綁定和驗證的數據。
          type Booking struct {
              CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
              CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"`
          }
          
          var bookableDate validator.Func=func(fl validator.FieldLevel) bool {
              date, ok :=fl.Field().Interface().(time.Time)
              if ok {
                  today :=time.Now()
                  if today.After(date) {
                      return false
                  }
              }
              return true
          }
          
          func main() {
              route :=gin.Default()
          
              if v, ok :=binding.Validator.Engine().(*validator.Validate); ok {
                  v.RegisterValidation("bookabledate", bookableDate)
              }
          
              route.GET("/bookable", getBookable)
              route.Run(":8085")
          }
          
          func getBookable(c *gin.Context) {
              var b Booking
              if err :=c.ShouldBindWith(&b, binding.Query); err==nil {
                  c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
              } else {
                  c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
              }
          }
          $ curl "localhost:8085/bookable?check_in=2018-04-16&check_out=2018-04-17"
          {"message":"Booking dates are valid!"}
          
          $ curl "localhost:8085/bookable?check_in=2018-03-08&check_out=2018-03-09"
          {"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}
          

          結構體級別的驗證器 也可以通過其他的方式注冊。更多信息請參閱 struct-lvl-validation 示例。

          設置和獲取 Cookie

          import (
              "fmt"
          
              "github.com/gin-gonic/gin"
          )
          
          func main() {
          
              router :=gin.Default()
          
              router.GET("/cookie", func(c *gin.Context) {
          
                  cookie, err :=c.Cookie("gin_cookie")
          
                  if err !=nil {
                      cookie="NotSet"
                      c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
                  }
          
                  fmt.Printf("Cookie value: %s \n", cookie)
              })
          
              router.Run()
          }
          

          路由參數

          func main() {
              router :=gin.Default()
          
              // 此 handler 將匹配 /user/john 但不會匹配 /user/ 或者 /user
              router.GET("/user/:name", func(c *gin.Context) {
                  name :=c.Param("name")
                  c.String(http.StatusOK, "Hello %s", name)
              })
          
              // 此 handler 將匹配 /user/john/ 和 /user/john/send
              // 如果沒有其他路由匹配 /user/john,它將重定向到 /user/john/
              router.GET("/user/:name/*action", func(c *gin.Context) {
                  name :=c.Param("name")
                  action :=c.Param("action")
                  message :=name + " is " + action
                  c.String(http.StatusOK, message)
              })
          
              router.Run(":8080")
          }
          

          路由組

          func main() {
              router :=gin.Default()
          
              // 簡單的路由組: v1
              v1 :=router.Group("/v1")
              {
                  v1.POST("/login", loginEndpoint)
                  v1.POST("/submit", submitEndpoint)
                  v1.POST("/read", readEndpoint)
              }
          
              // 簡單的路由組: v2
              v2 :=router.Group("/v2")
              {
                  v2.POST("/login", loginEndpoint)
                  v2.POST("/submit", submitEndpoint)
                  v2.POST("/read", readEndpoint)
              }
          
              router.Run(":8080")
          }
          

          運行多個服務

          請參閱 issues 并嘗試以下示例:

          package main
          
          import (
              "log"
              "net/http"
              "time"
          
              "github.com/gin-gonic/gin"
              "golang.org/x/sync/errgroup"
          )
          
          var (
              g errgroup.Group
          )
          
          func router01() http.Handler {
              e :=gin.New()
              e.Use(gin.Recovery())
              e.GET("/", func(c *gin.Context) {
                  c.JSON(
                      http.StatusOK,
                      gin.H{
                          "code":  http.StatusOK,
                          "error": "Welcome server 01",
                      },
                  )
              })
          
              return e
          }
          
          func router02() http.Handler {
              e :=gin.New()
              e.Use(gin.Recovery())
              e.GET("/", func(c *gin.Context) {
                  c.JSON(
                      http.StatusOK,
                      gin.H{
                          "code":  http.StatusOK,
                          "error": "Welcome server 02",
                      },
                  )
              })
          
              return e
          }
          
          func main() {
              server01 :=&http.Server{
                  Addr:         ":8080",
                  Handler:      router01(),
                  ReadTimeout:  5 * time.Second,
                  WriteTimeout: 10 * time.Second,
              }
          
              server02 :=&http.Server{
                  Addr:         ":8081",
                  Handler:      router02(),
                  ReadTimeout:  5 * time.Second,
                  WriteTimeout: 10 * time.Second,
              }
          
              g.Go(func() error {
                  return server01.ListenAndServe()
              })
          
              g.Go(func() error {
                  return server02.ListenAndServe()
              })
          
              if err :=g.Wait(); err !=nil {
                  log.Fatal(err)
              }
          }
          

          重定向

          HTTP 重定向很容易。 內部、外部重定向均支持。

          r.GET("/test", func(c *gin.Context) {
              c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
          })
          

          通過 POST 方法進行 HTTP 重定向。請參考 issue:#444

          r.POST("/test", func(c *gin.Context) {
              c.Redirect(http.StatusFound, "/foo")
          })
          

          路由重定向,使用 HandleContext:

          r.GET("/test", func(c *gin.Context) {
              c.Request.URL.Path="/test2"
              r.HandleContext(c)
          })
          r.GET("/test2", func(c *gin.Context) {
              c.JSON(200, gin.H{"hello": "world"})
          })
          

          靜態文件服務

          func main() {
              router :=gin.Default()
              router.Static("/assets", "./assets")
              router.StaticFS("/more_static", http.Dir("my_file_system"))
              router.StaticFile("/favicon.ico", "./resources/favicon.ico")
          
              // 監聽并在 0.0.0.0:8080 上啟動服務
              router.Run(":8080")
          }
          

          靜態資源嵌入

          你可以使用 go-assets 將靜態資源打包到可執行文件中。

          func main() {
              r :=gin.New()
          
              t, err :=loadTemplate()
              if err !=nil {
                  panic(err)
              }
              r.SetHTMLTemplate(t)
          
              r.GET("/", func(c *gin.Context) {
                  c.HTML(http.StatusOK, "/html/index.tmpl", nil)
              })
              r.Run(":8080")
          }
          
          // loadTemplate 加載由 go-assets-builder 嵌入的模板
          func loadTemplate() (*template.Template, error) {
              t :=template.New("")
              for name, file :=range Assets.Files {
                  if file.IsDir() || !strings.HasSuffix(name, ".tmpl") {
                      continue
                  }
                  h, err :=ioutil.ReadAll(file)
                  if err !=nil {
                      return nil, err
                  }
                  t, err=t.New(name).Parse(string(h))
                  if err !=nil {
                      return nil, err
                  }
              }
              return t, nil
          }
          

          請參閱 examples/assets-in-binary 目錄中的完整示例。

          用Golang流行的web框架Gin渲染HTML模板頁面的簡單例子

          Gin是Golang最流行的web框架之一。我之前已經寫過如何使用Golang基礎模板包渲染HTML頁面。使用Gin渲染HTML模板更加簡單。

          為了使工作流更加順暢,嘗試新的想法并進行調試,我還決定使用Codegangsta的自動重載工具Gin。

          安裝Gin

          安裝Gin HTTP web框架就像安裝大多數(如果不是所有)Golang包一樣簡單:

          go get -u github.com/gin-gonic/gin

          Gin 代碼

          package main
          
          import (
              "html/template"
              "net/http"
              "strings"
          
              "github.com/gin-gonic/gin"
          )
          
          func main() {
              router :=gin.Default()
              router.SetFuncMap(template.FuncMap{
                  "upper": strings.ToUpper,
              })
              router.Static("/assets", "./assets")
              router.LoadHTMLGlob("templates/*.html")
          
              router.GET("/", func(c *gin.Context) {
                  c.HTML(http.StatusOK, "index.html", gin.H{
                      "content": "This is an index page...",
                  })
              })
          
              router.GET("/about", func(c *gin.Context) {
                  c.HTML(http.StatusOK, "about.html", gin.H{
                      "content": "This is an about page...",
                  })
              })
              router.Run("localhost:8080")
          }

          引入的包

          在第4到7行,我們引入了一些包:

          • ? Gin HTTP web框架的包。
          • ? Golang的html/template基礎包,用于引入FuncMap()函數。在模板中使用函數時需要此函數。
          • ? 與Gin一起使用的Golang net/http基礎包。
          • ? strings基礎包,用于FuncMap中的upper函數。

          main()函數

          在第11行,我們創建了一個名為router的默認Gin路由。默認的Gin路由使用日志和恢復中間件,以及基本功能。

          在第12到14行,使用SetFuncMap()創建一個供模板使用的函數映射。這里我們添加了一個簡單的模板函數upper,它使用strings.ToUpper()函數將字符串中的所有字符設置為大寫。

          在第15行,我們讓Gin路由知道我們在./assets目錄中保存了一些靜態資產。Gin可以以這種方式訪問任何靜態資源。

          在這個例子中,我在那個目錄中放了一個Bulma CSS庫的最小版本。通過使用Static()函數,現在HTML模板可以訪問這個庫。

          在第16行,所有滿足template/*.html模式的模板都由LoadHTMLGlob()函數加載。這個模式意味著模板文件應該有.html的擴展名,并且位于/template目錄中。

          在第18到22行,我們告訴Gin路由接受URL路徑/上的HTTP GET方法請求。當收到請求時,Gin發送一個HTTP OK狀態消息,并用gin.H{}括號內提供的數據渲染index.html模板。在這種情況下,數據只包括一個鍵/值對,鍵名為content

          在第24到28行,與上面類似,我們告訴Gin路由接受/about路徑上的HTTP GET方法請求。這次渲染的是about.html模板。

          在第29行,我們讓Gin在localhost端口8080上運行web服務器。

          模板和目錄結構

          下面你可以找到本例中使用的四個模板。這些模板每個都需要在自己的文件中。每段代碼之前都有文件名。

          模板的語法與html/template基礎包中的相同。你可以我之前的文章中閱讀更多關于模板語法的內容。

          // header.html
          {{define "header.html"}}
          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8">
              <meta name="viewport" content="width=device-width, initial-scale=1">
              <link rel="stylesheet" type="text/css" href="/assets/bulma.min.css">
              <title></title>
          </head>
          <body>
          <div class="container">
          <br><br>
          {{end}}
            
          
          // footer.html
          {{define "footer.html"}}
          </div>
          </body>
          </html>
          {{end}}
          
          
          // index.html
          {{template "header.html"}}
          <h1 class="title">
              INDEX PAGE
          </h1>
          <p>{{ .content }}</p>
          {{template "footer.html"}}
          
          
          // about.html
          {{template "header.html"}}
          <h1 class="title">
              ABOUT PAGE
          </h1>
          <p>{{ .content | upper}}</p>
          {{template "footer.html"}}

          自動重載Gin

          如前所述,我在開發時使用codegangsta/gin工具來自動重載gin。這使得在瀏覽器中檢查我們代碼的結果變得更加容易。當我們改變了代碼,無需停止當前的可執行文件,重新構建和運行它,就可以做到這一點。

          以下是Github頁面上對這個工具的描述:

          gin是一個簡單的命令行工具,用于實時重新加載Go web應用程序。只需在你的應用程序目錄下運行gin,你的web應用就會以gin作為代理來服務。gin會在檢測到更改時自動重新編譯你的代碼。下次它接收到HTTP請求時,你的應用將會重啟。

          安裝codegangsta/gin

          go get github.com/codegangsta/gin

          運行codegangsta/gin

          gin -i --appPort 8080 --port 3000 run main.go

          上述代碼需要在命令行中運行。它假設你正在localhost8080端口上運行你的Go web應用,并且你將使用3000端口作為Gin自動重載代理。

          關于運行代碼的注意事項

          在從命令行運行代碼之前,你可能需要運行以下命令:

          go mod init
          go mod tidy

          在網頁瀏覽器中檢查結果

          在瀏覽器的URL字段中輸入以下內容以檢查代碼的結果:

          localhost:3000/

          …對于索引頁面,和…

          localhost:3000/about

          對于關于頁面。

          列表清單

          請記住,始終保持學習的態度,并享受編碼的樂趣!祝您編碼愉快!

          如果你喜歡我的文章,點贊,關注,轉發!

          Gin 是一個 Golang 寫的 web 框架,具有高性能的優點,基于 httprouter,它提供了類似martini但更好性能(路由性能約快40倍)的API服務。官方地址:https://github.com/gin-gonic/gin。

          使用

          在項目中使用Gin也極其簡單

          1. 下載gin: go get -u -v github.com/gin-gonic/gin
          2. import中引入"github.com/gin-gonic/gin"
          3. 核心代碼r :=gin.Default()r.GET("/ping", ping) r.Run(":8080")
          4. go run 后,瀏覽器輸入http://localhost:9090/ping即可使用
          5. 可以使用熱加載調試 工具:go get -v -u github.com/pilu/fresh - 執行fresh即可,代碼有變動會自動編譯程序

          下面為整體演示代碼:

          package main
          import (
            "net/http"
            ginSwagger "github.com/swaggo/gin-swagger"
            "github.com/swaggo/gin-swagger/swaggerFiles"
            _ "asap/docs"
            "github.com/gin-gonic/gin"
          )
          // @Summary 接口探活
          // @Produce  json
          // @Param lang query string false "en"
          // @Success 200 {string} string "ok"
          // @Router /ping [get]
          func ping(c *gin.Context) {
            c.String(http.StatusOK, "ok")
          }
          func main() {
            r :=gin.Default()
            r.GET("/ping", ping)
            r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
            v1 :=r.Group("/v1")
          {
              v1.GET("/group", ping)
          }
            // Listen and Server in 0.0.0.0:8080
            r.Run(":9090")
          }

          源碼剖析

          Gin代碼量很少,使用 find . -name "*.go" | xargs cat | wc -l 查看后,在14K左右。我會從5個方面來進行解析

          • 結構簡介:Gin使用的結構進行簡單說明
          • 前期準備:Gin是如何將路由等信息組合起來的
          • 監聽請求:Gin是如何開啟服務,監聽請求的
          • 請求處理:當請求到達時,Gin如何處理這些請求
          • 返回數據:處理完請求后,如何將數據返回給請求方

          本文只是做簡單的剖析,方便大家能夠快速地理解和學習這個框架。

          相關結構和流程圖可參考:

          https://www.processon.com/view/link/5e36f9efe4b0d27af1852f3b

          https://www.processon.com/view/link/5e3fc3d8e4b06b291a662a26

          結構簡介

          本節主要給大家介紹一下Gin使用的核心數據結構或者接口,其中有些圖畫得不太準確,不過不影響理解。

          1. Engine:Engine里有三個非常重要的數據
          2. RouterGroup:使用Handlers存放中間件,另外提供設置路由的功能
          3. pool:主要用于從池中獲取或者存放Context,減少GC
          4. trees:主要用于存放路由信息和該路由對應的處理函數列表,每個HTTP方法(GET POST等)都有單獨的tree

          1. Context:主要接收server傳遞過來的http.Request和http.Response,并對這兩個數據進行處理

          四個流程

          紅色為前期準備,綠色為監聽請求,紫色為請求處理,粉色為返回數據。如果圖片不清晰,可查看 https://www.processon.com/view/link/5e36f9efe4b0d27af1852f3b

          說明

          1. 學習完Gin框架,我們自己也可以制作簡單的go-web框架。go在語言層面幫我們解決了很多重復性的工作,實在是一門有理想有追求的語言。
          2. 本文章只是簡單的講解,最好的剖析還是源碼本身。


          參考資料

          使用

          1. https://geektutu.com/post/quick-go-gin.html Go Gin 簡明教程
          2. https://github.com/gin-gonic/gin 源碼
          3. https://gin-gonic.com/zh-cn/docs/ 中文文檔
          4. https://www.kancloud.cn/shuangdeyu/gin_book/949436 gin中文文檔

          源碼

          1. https://www.jianshu.com/p/35addb4de300
          2. https://www.cnblogs.com/yjf512/p/9670990.html
          3. https://www.cnblogs.com/sunsky303/p/9706210.html sync.Pool詳解

          主站蜘蛛池模板: 香蕉视频一区二区三区| 中文字幕一区在线观看| 精品久久国产一区二区三区香蕉| 亚洲啪啪综合AV一区| 精品久久综合一区二区| 午夜影视日本亚洲欧洲精品一区| 人妻无码一区二区三区四区| 国产亚洲一区二区三区在线不卡| 一区 二区 三区 中文字幕| 亚洲AV综合色区无码一区| 国产一区二区三区91| 欧美激情国产精品视频一区二区| 国产一区二区福利| 日韩人妻无码一区二区三区| 国产一区二区三区在线观看免费 | 精品国产高清自在线一区二区三区| 国产探花在线精品一区二区| 上原亚衣一区二区在线观看| 亚洲福利视频一区二区| 无码精品前田一区二区| 亚洲国产高清在线一区二区三区| 一区二区不卡视频在线观看| 亚洲国产一区二区视频网站| 一级特黄性色生活片一区二区 | 国产精品成人一区二区三区| 成人毛片无码一区二区| 精品久久综合一区二区| 无码AV一区二区三区无码| 亚洲福利视频一区二区| 亚洲一区二区三区偷拍女厕| 蜜桃臀无码内射一区二区三区| 亚洲福利电影一区二区?| 亚洲国产精品一区二区三区在线观看 | 国产成人久久精品区一区二区 | 亚洲国产欧美国产综合一区 | 国产人妖视频一区在线观看| 国产在线精品一区在线观看| 国产乱码伦精品一区二区三区麻豆 | 国产高清一区二区三区视频| 国产成人精品久久一区二区三区av | 亚洲一区二区三区国产精品|