十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊
量身定制 + 運營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
這篇文章主要講解了“Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識點總結(jié)”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識點總結(jié)”吧!
創(chuàng)新互聯(lián)專注于恩平企業(yè)網(wǎng)站建設(shè),自適應(yīng)網(wǎng)站建設(shè),商城開發(fā)。恩平網(wǎng)站建設(shè)公司,為恩平等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站制作,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
重定向
Gin返回一個HTTP重定向非常簡單, 使用Redirect方法即可. 內(nèi)部和外部鏈接都支持.
package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/test", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") //重定向到外部鏈接 }) //重定向到內(nèi)部鏈接 r.GET("/internal", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/home") }) r.GET("/home", func(c *gin.Context) { c.JSON(200, gin.H{"msg": "這是首頁"}) }) r.Run(":8080") } /* 重定向到外部鏈接,訪問:http://localhost:8080/test 重定向到內(nèi)部鏈接,訪問:http://localhost:8080/internal */
從POST方法中完成HTTP重定向, 參考問題#444 https://github.com/gin-gonic/gin/issues/444
r.POST("/test", func(c *gin.Context) { c.Redirect(http.StatusFound, "/foo") })
如果要產(chǎn)生一個路由重定向, 類似上面的內(nèi)部重定向, 則使用 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"}) })
自定義中間件
package main import ( "github.com/gin-gonic/gin" "log" "time" ) //自定義日志中間件 func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // Set example variable 在gin上下文中設(shè)置鍵值對 c.Set("example", "12345") // before request //Next方法只能用于中間件中,在當(dāng)前中間件中, 從方法鏈執(zhí)行掛起的處理器 c.Next() // after request 打印中間件執(zhí)行耗時 latency := time.Since(t) log.Print(latency) // access the status we are sending 打印本中間件的狀態(tài)碼 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) //從上下文中獲取鍵值對 // it would print: "12345" log.Println(example) }) // Listen and serve on 0.0.0.0:8080 r.Run(":8080") }
使用基本認(rèn)證BasicAuth()中間件
package main import ( "github.com/gin-gonic/gin" "net/http" ) // simulate some private data 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() // Group using gin.BasicAuth() middleware // gin.Accounts is a shortcut for map[string]string // 路由組authorized使用基本認(rèn)證中間件, 參數(shù)為gin.Accounts,是一個map,鍵名是用戶名, 鍵值是密碼, 該中間件會將認(rèn)證信息保存到cookie中 authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ "foo": "bar", "austin": "1234", "lena": "hello2", "manu": "4321", })) // /admin/secrets endpoint // hit "localhost:8080/admin/secrets authorized.GET("/secrets", func(c *gin.Context) { // get user, it was set by the BasicAuth middleware // 從cookie中獲取用戶認(rèn)證信息, 鍵名為user 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 :("}) } }) // Listen and serve on 0.0.0.0:8080 r.Run(":8080") } /* 測試訪問:http://localhost:8080/admin/secrets */
在中間件中使用協(xié)程Goroutines
在中間件或者控制器中啟動新協(xié)程時, 不能直接使用原來的Gin上下文, 必須使用一個只讀的上下文副本
package main import ( "github.com/gin-gonic/gin" "log" "time" ) func main() { r := gin.Default() r.GET("/long_async", func(c *gin.Context) { // create copy to be used inside the goroutine // 創(chuàng)建一個Gin上下文的副本, 準(zhǔn)備在協(xié)程Goroutine中使用 cCp := c.Copy() go func() { // simulate a long task with time.Sleep(). 5 seconds // 模擬長時間任務(wù),這里是5秒 time.Sleep(5 * time.Second) // note that you are using the copied context "cCp", IMPORTANT // 在中間件或者控制器中啟動協(xié)程時, 不能直接使用原來的上下文, 必須使用一個只讀的上線文副本 log.Println("Done! in path " + cCp.Request.URL.Path) }() }) r.GET("/long_sync", func(c *gin.Context) { // simulate a long task with time.Sleep(). 5 seconds time.Sleep(5 * time.Second) // since we are NOT using a goroutine, we do not have to copy the context // 沒有使用協(xié)程時, 可以直接使用Gin上下文 log.Println("Done! in path " + c.Request.URL.Path) }) // Listen and serve on 0.0.0.0:8080 r.Run(":8080") } /* 模擬同步阻塞訪問:http://localhost:8080/long_sync 模擬異步非阻塞訪問:http://localhost:8080/long_async */
自定義HTTP配置
直接使用 http.ListenAndServe()方法:
func main() { router := gin.Default() http.ListenAndServe(":8080", router) }
或者自定義HTTP配置
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() }
支持Let'sEncrypt證書加密處理HTTPS
下面是一行式的LetsEncrypt HTTPS服務(wù)
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") }) //一行式LetsEncrypt證書, 處理https 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, //Prompt指定一個回調(diào)函數(shù)有條件的接受證書機(jī)構(gòu)CA的TOS服務(wù), 使用AcceptTOS總是接受服務(wù)條款 HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), //HostPolicy用于控制指定哪些域名, 管理器將檢索新證書 Cache: autocert.DirCache("/var/www/.cache"), //緩存證書和其他狀態(tài) } log.Fatal(autotls.RunWithManager(r, &m)) }
詳情參考autotls包
使用Gin運行多個服務(wù)
可以在主函數(shù)中使用協(xié)程Goroutine運行多個服務(wù), 每個服務(wù)端口不同, 路由分組也不同. 請參考這個問題, 嘗試運行以下示例代碼:
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 { err := server01.ListenAndServe() if err != nil && err != http.ErrServerClosed { log.Fatal(err) } return err }) g.Go(func() error { err := server02.ListenAndServe() if err != nil && err != http.ErrServerClosed { log.Fatal(err) } return err }) if err := g.Wait(); err != nil { log.Fatal(err) } } /* 模擬訪問服務(wù)1: curl http://localhost:8080/ {"code":200,"error":"Welcome server 01"} 模擬訪問服務(wù)2: curl http://localhost:8081/ {"code":200,"error":"Welcome server 02"} */
優(yōu)雅的關(guān)閉和重啟服務(wù)
有一些方法可以優(yōu)雅的關(guān)閉或者重啟服務(wù), 比如不應(yīng)該中斷活動的連接, 需要優(yōu)雅等待服務(wù)完成后才執(zhí)行關(guān)閉或重啟. 你可以使用第三方包來實現(xiàn), 也可以使用內(nèi)置的包自己實現(xiàn)優(yōu)雅關(guān)閉或重啟.
使用第三方包
fvbock/endless 包, 可以實現(xiàn)Golang HTTP/HTTPS服務(wù)的零停機(jī)和優(yōu)雅重啟(Golang版本至少1.3以上)
我們可以使用fvbock/endless 替代默認(rèn)的 ListenAndServe方法, 更多詳情, 請參考問題#296.
router := gin.Default() router.GET("/", handler) // [...] endless.ListenAndServe(":4242", router)
其他替代包:
manners: 一個優(yōu)雅的Go HTTP服務(wù), 可以優(yōu)雅的關(guān)閉服務(wù).
graceful: 優(yōu)雅的Go包, 能夠優(yōu)雅的關(guān)閉一個http.Handler服務(wù)
grace: 該包為Go服務(wù)實現(xiàn)優(yōu)雅重啟, 零停機(jī)
手動實現(xiàn)
如果你使用Go1.8或者更高的版本, 你可能不需要使用這些庫. 可以考慮使用http.Server的內(nèi)置方法Shutdown()來優(yōu)雅關(guān)閉服務(wù). 下面的示例描述了基本用法, 更多示例請參考這里
// +build go1.8 package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "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, } // Initializing the server in a goroutine so that // it won't block the graceful shutdown handling below // 用協(xié)程初始化一個服務(wù), 它不會阻塞下面的優(yōu)雅邏輯處理 go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("listen: %s\n", err) } }() // Wait for interrupt signal to gracefully shutdown the server with // a timeout of 5 seconds. //等待一個操作系統(tǒng)的中斷信號, 來優(yōu)雅的關(guān)閉服務(wù) quit := make(chan os.Signal) // kill (no param) default send syscall.SIGTERM //kill會發(fā)送終止信號 // kill -2 is syscall.SIGINT //發(fā)送強(qiáng)制進(jìn)程結(jié)束信號 // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it //發(fā)送SIGKILL信號給進(jìn)程 signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit //阻塞在這里,直到獲取到一個上面的信號 log.Println("Shutting down server...") // The context is used to inform the server it has 5 seconds to finish // the request it is currently handling //這里使用context上下文包, 有5秒鐘的處理超時時間 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { //利用內(nèi)置Shutdown方法優(yōu)雅關(guān)閉服務(wù) log.Fatal("Server forced to shutdown:", err) } log.Println("Server exiting") }
感謝各位的閱讀,以上就是“Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識點總結(jié)”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識點總結(jié)這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!