Gin 文档学习
1.自定義模板渲染器
// 自定義html模板渲染器,要指定所有的html路徑,不推薦html := template.Must(template.ParseFiles("templates/login.html","templates/users/index.html","templates/center/index.html",))
//應用這些模板router.SetHTMLTemplate(html)router.GET("/users/index", func(context *gin.Context) {context.HTML(http.StatusOK, "users/index.html", gin.H{"title": "users/index.html",})})
2.自定義模板功能
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("./templates/raw.html")router.GET("/raw", func(c *gin.Context) {c.HTML(http.StatusOK, "raw.html", map[string]interface{}{"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),})})router.Run(":9999")
}
raw.html
<body>
<!--等同于 formatAsDate .now-->
date: <{.now | formatAsDate}>
</body>
3.Multipart/Urlencoded 綁定
package mainimport ("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 == "Winnie-OCEAN" && form.Password == "789" {c.JSON(200, gin.H{"status": "you are logged in","user": form.User,"password": form.Password,})} else {c.JSON(401, gin.H{"status": "unauthorized"})}}})router.Run()
}
或者
router.POST("/login", func(c *gin.Context) {// 或者簡單地使用 ShouldBind 方法自動綁定://c.ShouldBind(&form)var form LoginForm// 你可以使用顯式綁定聲明綁定 multipart form:if c.ShouldBindWith(&form, binding.Form) == nil {if form.User == "OCEAN" && form.Password == "123456789" {c.JSON(200, gin.H{"status": "you are logged in","user": form.User,"password": form.Password,})} else {c.JSON(401, gin.H{"status": "unauthorized"})}}})
4.Multipart/Urlencoded 表單
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {engine := gin.Default()engine.POST("/post_form", func(context *gin.Context) {//獲取post過來的名為user的值user := context.PostForm("user")//獲取post過來的名為password的值,第二個參數為默認值password := context.DefaultPostForm("password", "123456")context.JSON(http.StatusOK, gin.H{"success": "login in","username": user,"password": password,})})engine.Run()
}
5.PureJSON
JSON 使用 unicode 替換特殊 HTML 字符,例如 < 變為 \ u003c。如果要按字面對這些字符進行編碼,則可以使用 PureJSON。
package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()//結果為 {"html":"\u003cb\u003eHello, world!\u003c/b\u003e"}// 提供 unicode 實體r.GET("/json", func(c *gin.Context) {c.JSON(200, gin.H{"html": "<b>Hello, world!</b>",})})//輸出結果{"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")
}
6.提取url參數同時獲取post或來的數據 Query 和 post form
package mainimport "github.com/gin-gonic/gin"func main() {engine := gin.Default()engine.POST("/post_query", func(context *gin.Context) {username := context.Query("username")password := context.DefaultQuery("password", "123")age := context.PostForm("age")page := context.PostForm("page")context.JSON(200, gin.H{"username": username,"password": password,"age": age,"page": page,})})engine.Run(":9999")
}
7.SecureJSON 防止 json 劫持
JSON劫持,其實就是惡意網站,通過<script>標簽獲取你的JSON數據,因為JSON數組默認為是可執行的JS,所以通過這種方式,可以獲得你的敏感數據。最前面有個while(1);前綴,這就可以在<script>標簽執行我們返回的數據時,就可以無限循環,阻止后面數組數據的執行,防止數據被劫持。
func main() {r := gin.Default()r.GET("/someJSON", func(c *gin.Context) {names := []string{"winnie", "ocean", "22"}// 將輸出:while(1);["winnie", "ocean", "22"]c.SecureJSON(http.StatusOK, names)})// 監聽并在 0.0.0.0:8080 上啟動服務r.Run(":8080")
}
Gin默認的防JSON劫持的前綴是while(1);我們可以改變,通過r.SecureJsonPrefix方法設置即可,如:
func main() {r := gin.Default()//你也可以使用自己的 SecureJSON 前綴r.SecureJsonPrefix(")]}',\n")r.GET("/someJSON", func(c *gin.Context) {names := []string{"winnie", "ocean", "22"}// 將輸出://)]}',//["winnie","ocean","22"]c.SecureJSON(http.StatusOK, names)})r.Run(":8080")
}
8. xml json yaml protobuf
package mainimport ("github.com/gin-gonic/gin""github.com/gin-gonic/gin/testdata/protoexample""net/http"
)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 {// 注意 msg.Name 在 JSON 中變成了 "user"Name string `json:"user"`Message stringNumber int}msg.Name = "Lena"msg.Message = "hey"msg.Number = 123// 將輸出:{"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,}// 請注意,數據在響應中變為二進制數據// 將輸出(產生) data被ProtoBuf序列化了 的數據,生成一個二進制文件// 序列化:將數據結構或者對象轉化成二進制串的過程c.ProtoBuf(http.StatusOK, data)})r.Run()
}
9.上傳文件
9.1 單文件
func main() {engine := gin.Default()// 為 multipart forms 設置較低的內存限制 (默認是 32 MiB)// 限制處理該文件所占用的最大內存engine.MaxMultipartMemory = 8 << 20 // 8 MiBengine.POST("/post", func(context *gin.Context) {//單文件file, err := context.FormFile("picture")if err != nil {log.Fatal(err)}//打印文件名log.Println(file.Filename)//設置上傳路徑 沒有就默認當前目錄下dst := "./" + file.Filename// 上傳文件至指定的完整文件路徑context.SaveUploadedFile(file, dst)context.String(200, "upload ok!!")})engine.Run()
}
9.2 多文件
func main() {engine := gin.Default()engine.MaxMultipartMemory = 8 << 20 //8Mibengine.POST("/files", func(context *gin.Context) {//MultipartForm是經過解析的 多部分表單,包括文件上傳。form, _ := context.MultipartForm() //多部份表單files := form.File["uploads[]"] //收集名為 uploads[]的多個文件//循環遍歷輸出文件名for _, file := range files {log.Println(file.Filename)//上傳文件 至 指定路徑 沒有路徑,默認根目錄。context.SaveUploadedFile(file, file.Filename)}context.String(200, "upload files ok!!")})engine.Run()
}
10.從 reader 讀取數據
訪問http://localhost:8080/test后,將下載url地址的圖片并將其命名為123.png 但現在將其下載卻打不開
func main() {router := gin.Default()router.GET("/test", func(c *gin.Context) {response, err := http.Get("https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%BC%A0%E7%B4%AB%E5%AE%81%E5%9B%BE%E7%89%87&hs=0&pn=1&spn=0&di=7136437450519347201&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=2643363613%2C2106712228&os=3028774708%2C1970574687&simid=2643363613%2C2106712228&adpicid=0&lpn=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=0&oriquery=%E5%BC%A0%E7%B4%AB%E5%AE%81%E5%9B%BE%E7%89%87&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fn.sinaimg.cn%2Fsinakd20111%2F539%2Fw1960h2579%2F20210619%2F9e65-74af1bfc3b1873479616e5a37bb490eb.jpg%26refer%3Dhttp%3A%2F%2Fn.sinaimg.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1666405458%26t%3D02eb7dfb10fad465e8d88e94d0074659&fromurl=ippr_z2C%24qAzdH3FAzdH3Fh_z%26e3Bftgw_z%26e3Bv54_z%26e3BvgAzdH3Fw6ptvsj_0n9lnd0n8n_8kma11c18aa8aapj4p_z%26e3Bip4s&gsm=2&islist=&querylist=&dyTabStr=MCwzLDYsMiwxLDQsNSw4LDcsOQ%3D%3D.jpg")// StatusCode 響應體的狀態碼if err != nil || response.StatusCode != http.StatusOK {//Status設置HTTP響應碼 503c.Status(http.StatusServiceUnavailable)return}reader := response.Body //響應體contentLength := response.ContentLengthcontentType := response.Header.Get("Content-Type") //獲得 響應體數據類型extraHeaders := map[string]string{"Content-Disposition": `attachment; filename="123.png"`,}// DataFromReader 將指定的讀取器寫入主體流 并 更新HTTP代碼c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)})router.Run(":8080")
}
11. BasicAuth 中間件 驗證用戶登錄
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/secretsauthorized.GET("/secrets", func(c *gin.Context) {// 獲取用戶,它是由 BasicAuth 中間件設置的//如果給定鍵存在,MustGet將返回該鍵的值,否則將出現panic。user := c.MustGet(gin.AuthUserKey).(string) //獲取輸入的用戶名//secret 對應鍵名為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 :("})}})// 監聽并在 0.0.0.0:8080 上啟動服務r.Run(":8080")
}
12.嵌套路由組
func main() {// 新建一個沒有任何默認中間件的路由r := gin.New()authorized := r.Group("/")authorized.Use(readEndpoint){authorized.POST("/login", loginEndpoint)}// 嵌套路由組//訪問 http://localhost:8080/testing/analytics 得到結果testing := authorized.Group("testing")testing.GET("/analytics", analyticsEndpoint)// 監聽并在 0.0.0.0:8080 上啟動服務r.Run(":8080")
}
13.只綁定 url 查詢字符串
ShouldBindQuery 函數只綁定 url 查詢參數而忽略 post 數據。
type Person struct {Name string `form:"name"`Address string `form:"address"`
}func main() {route := gin.Default()//任何都注冊一個與所有HTTP方法匹配的路由。獲取,發布,放置,修補,頭部,選項,刪除,連接,跟蹤route.Any("/testing", startPage)route.Run()
}func startPage(c *gin.Context) {var person Person//ShouldBindQuery是c.ShouldBindWith(obj, binding.Query)的快捷方式。if c.ShouldBindQuery(&person) == nil {log.Println("====== Only Bind By Query String ======")log.Println(person.Name)log.Println(person.Address)}c.String(200, "Success\n")c.JSON(200, gin.H{"address": person.Address,"name": person.Name,})
}
14 在中間件中使用 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")
}
15 如何記錄日志
func main() {// 禁用控制臺顏色,將日志寫入文件時不需要控制臺顏色。gin.DisableConsoleColor()// Create 創建一個名為 Winne.log 的文件f, _ := os.Create("2.log")// DefaultWriter=os.Stdout 指向系統的標準輸出//將日志記錄到文件 fgin.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")
}
16 將 request body 綁定到不同的結構體中
要想多次綁定,可以使用 c.ShouldBindBodyWith. 只有某些格式需要此功能,如 JSON, XML, MsgPack, ProtoBuf。
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.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 {...}
}
17 映射查詢字符串或表單參數
func main() {router := gin.Default()router.POST("/post", func(c *gin.Context) {ids := c.QueryMap("ids") //url參數names := c.PostFormMap("names")//post過來的數據fmt.Printf("ids: %v; names: %v", ids, names)c.JSON(200, ids)c.JSON(200, names)})router.Run()
}
18 查詢字符串參數 Query DefaultQuery
func main() {router := gin.Default()// 使用現有的基礎請求對象解析查詢字符串參數。// 示例 URL: /welcome?firstname=winnie&lastname=jinerouter.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")
}
19 模型綁定和驗證
19.1 JSON
// 綁定 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": "winnie", "password": "123"})router.POST("/login", func(c *gin.Context) {var json Loginif err := c.ShouldBindJSON(&json); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if json.User != "winnie" || json.Password != "123" {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})c.JSON(200, json)})router.Run()
}
19.2 XML
// 綁定 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()router.POST("/loginXML", func(c *gin.Context) {var xml Loginif err := c.ShouldBindXML(&xml); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if xml.User != "OCEAN" || xml.Password != "123" {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})c.JSON(200, xml)})router.Run()
}
19.3 綁定html表單
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()// 綁定 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"})c.JSON(200, form)})router.Run()
}
20 綁定 Uri
type Person struct {ID string `uri:"id" binding:"required"`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.Error()})return}c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})})route.Run()
}
21 綁定查詢字符串或表單數據
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()
}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#L88if c.ShouldBind(&person) == nil {log.Println(person.Name)log.Println(person.Address)log.Println(person.Birthday)}c.JSON(200, person)c.String(200, "Success")
}
如果 采用post方法
22 綁定表單數據至自定義結構體
func main() {r := gin.Default()r.GET("/getb", func(context *gin.Context) {var b StructB//shouldbind與bind作用一致,但是 shouldbind相比較于bind能更好的控制綁定context.ShouldBind(&b)context.JSON(200, gin.H{"f_a": b.NestedStruct.FieldA,"f_b": b.FiledB,})})r.GET("/getc", func(context *gin.Context) {var c StructCcontext.ShouldBind(&c)context.JSON(200, gin.H{//"f_a": (*(c.NestedStructPointer)).FieldA"f_a": c.NestedStructPointer.FieldA,"f_c": c.FieldC,})})r.GET("/getd", func(context *gin.Context) {var d StructDcontext.ShouldBind(&d)context.JSON(200, gin.H{"f_x": d.NestedAnnoyStruct.FieldX,"f_d": d.FieldD,})})r.Run()
}
目前僅支持沒有 form 的嵌套結構體 ,例如 下列 不支持以下格式的結構體
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
}
23 自定義 HTTP 配置
func main() {router := gin.Default()router.GET("/", func(context *gin.Context) {context.String(200, "自定義配置成功")})http.ListenAndServe(":8080", router)
}
func main() {router := gin.Default()router.GET("/", func(context *gin.Context) {context.String(200, "配置成功")})s := &http.Server{Addr: ":9999",Handler: router,ReadTimeout: 10 * time.Second,WriteTimeout: 10 * time.Second,MaxHeaderBytes: 1 << 20,}s.ListenAndServe()
}
24 自定義中間件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()// 設置 example 變量c.Set("example", "12345")// 請求前c.Next()// 請求后// Since返回從 t 開始經過的時間。latency := time.Since(t)log.Print(latency)// 獲取發送的 status Status返回當前請求的HTTP響應狀態碼。status := c.Writer.Status()log.Println(status)}
}func main() {r := gin.New()//或者 r.Use(Logger())r.GET("/test", Logger(), func(c *gin.Context) {//如果給定鍵存在,MustGet將返回該鍵的值,example := c.MustGet("example").(string)// 打印:"12345"log.Println(example)})// 監聽并在 0.0.0.0:8080 上啟動服務r.Run(":9999")
}
25 自定義日志文件
func main() {router := gin.New()// LoggerWithFormatter 中間件會寫入日志到 gin.DefaultWriter// 默認 gin.DefaultWriter = os.Stdout//LoggerWithFormatter實例 是 一個Logger中間件,具有 指定的日志格式功能。//LogFormatterParams是任何格式化程序在需要記錄日志時都會提交的結構router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {// 你的自定義格式return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",param.ClientIP,param.TimeStamp.Format(time.RFC1123),param.Method,param.Path,param.Request.Proto,param.StatusCode,param.Latency,param.Request.UserAgent(),param.ErrorMessage,)}))router.Use(gin.Recovery())router.GET("/ping", func(c *gin.Context) {c.String(200, "pong")})router.Run(":8080")
}
26 自定義驗證器
這個自定義驗證器 驗證流程有兩個要素
一:需要傳入兩次時間 分別是 checkin與checkout 每傳入一次, bookableDate 都需要驗證一次
二:gtfield=CheckIn 用來約束 check_out時間 大于 check_in時間。
// Booking 包含綁定和驗證的數據。
type Booking struct {CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`// gtfield 是一個默認規則,意思是要大于某個字段的值. gtfield=CheckIn表明 check_out的值要大于check_in的值CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"`
}// 定義一個驗證方法,用來驗證時間是否合法
// 驗證方法返回值應該是個布爾值
// type Func func(fl FieldLevel) bool
// FieldLevel 包含驗證字段的所有信息和幫助函數
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {// Field 返回當前字段進行驗證// 將值以 interface{} 類型返回date, ok := fl.Field().Interface().(time.Time)if ok {today := time.Now()//判斷today是否在date之后 如果是 返回 true,否則返回 falseif today.After(date) {return false}}return true
}func main() {route := gin.Default()// Validator 是實現 StructValidator 接口的 默認驗證器.// Engine 返回為StructValidator實現提供動力 的 底層驗證器引擎。// Validate 包含 驗證器設置和緩存// if語句 表示 是否驗證器設置成功if v, ok := binding.Validator.Engine().(*validator.Validate); ok {//注冊一個自定義驗證方法 bookabledatev.RegisterValidation("bookabledate", bookableDate)}route.GET("/bookable", getBookable)route.Run(":8085")
}func getBookable(c *gin.Context) {var b Bookingif 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()})}
}
27 設置獲取 cookie
func main() {router := gin.Default()router.GET("/cookie", func(c *gin.Context) {// 獲取cookiecookie, err := c.Cookie("gin_cookie")//表示 獲取出錯if err != nil {cookie = "NotSet"c.SetCookie("gin_cookie", "winnie", 3600, "/", "localhost", false, true)}fmt.Printf("Cookie value: %s \n", cookie)})router.Run()
}
第一次的 Cookie value: NotSet 原因:剛開始沒獲取到,所以cookie 值設為NotSet,并且重新設置了cookie,因為重新設置后并沒有獲取cookie,所以 cookie的值仍然是NotSet。輸出為 Cookie value: NotSet
第二次 獲取到cookie了 所以打印成功。
28 路由參數
func main() {router := gin.Default()// 此 handler 將匹配 /user/john 但不會匹配 /user/ 或者 /userrouter.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 " + actionc.String(http.StatusOK, message)})router.Run(":8080")
}
29 一個程序是否可以服務兩個端口
下面這個代碼是只能運行80端口,不能運行90端口。
func main() {router := gin.Default()router.GET("/", func(c *gin.Context) {c.JSON(200, "我是8080")})router.Run(":8080") // data servicesrouterAdmin := gin.Default()routerAdmin.GET("/", func(c *gin.Context) {c.JSON(200, "我是8090")})routerAdmin.Run(":8090") // admin and monitor services
}
下面這個代碼能同時運行80 90端口
func main() {router := gin.Default()router.GET("/", func(c *gin.Context) {c.JSON(200, "我是80端口")})go router.Run(":8080") // 開啟一個攜程routerAdmin := gin.Default()routerAdmin.GET("/", func(c *gin.Context) {c.JSON(200, "我是90端口")})routerAdmin.Run(":8090") // admin and monitor services
}
gin文檔示例代碼
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()})// Wait 等待阻塞,直到所有來自Go方法的函數調用都返回,然后從它們返回第一個非nil錯誤(如果有的話)。if err := g.Wait(); err != nil {log.Fatal(err)}
}
30
31
32
33
34
35
36
37
38
39
總結
- 上一篇: C#与mongoDB初始环境搭建
- 下一篇: Git 从了解到放弃