您好,登錄后才能下訂單哦!
這篇文章主要講解了“Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識(shí)點(diǎn)總結(jié)”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識(shí)點(diǎn)總結(jié)”吧!
重定向
Gin返回一個(gè)HTTP重定向非常簡(jiǎn)單, 使用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": "這是首頁(yè)"}) }) r.Run(":8080") } /* 重定向到外部鏈接,訪問(wèn):http://localhost:8080/test 重定向到內(nèi)部鏈接,訪問(wèn):http://localhost:8080/internal */
從POST方法中完成HTTP重定向, 參考問(wèn)題#444 https://github.com/gin-gonic/gin/issues/444
r.POST("/test", func(c *gin.Context) { c.Redirect(http.StatusFound, "/foo") })
如果要產(chǎn)生一個(gè)路由重定向, 類(lèi)似上面的內(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è)置鍵值對(duì) c.Set("example", "12345") // before request //Next方法只能用于中間件中,在當(dāng)前中間件中, 從方法鏈執(zhí)行掛起的處理器 c.Next() // after request 打印中間件執(zhí)行耗時(shí) 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) //從上下文中獲取鍵值對(duì) // 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,是一個(gè)map,鍵名是用戶名, 鍵值是密碼, 該中間件會(huì)將認(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") } /* 測(cè)試訪問(wèn):http://localhost:8080/admin/secrets */
在中間件中使用協(xié)程Goroutines
在中間件或者控制器中啟動(dòng)新協(xié)程時(shí), 不能直接使用原來(lái)的Gin上下文, 必須使用一個(gè)只讀的上下文副本
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)建一個(gè)Gin上下文的副本, 準(zhǔn)備在協(xié)程Goroutine中使用 cCp := c.Copy() go func() { // simulate a long task with time.Sleep(). 5 seconds // 模擬長(zhǎng)時(shí)間任務(wù),這里是5秒 time.Sleep(5 * time.Second) // note that you are using the copied context "cCp", IMPORTANT // 在中間件或者控制器中啟動(dòng)協(xié)程時(shí), 不能直接使用原來(lái)的上下文, 必須使用一個(gè)只讀的上線文副本 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 // 沒(méi)有使用協(xié)程時(shí), 可以直接使用Gin上下文 log.Println("Done! in path " + c.Request.URL.Path) }) // Listen and serve on 0.0.0.0:8080 r.Run(":8080") } /* 模擬同步阻塞訪問(wèn):http://localhost:8080/long_sync 模擬異步非阻塞訪問(wèn):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證書(shū)加密處理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證書(shū), 處理https log.Fatal(autotls.Run(r, "example1.com", "example2.com")) }
自定義自動(dòng)證書(shū)管理器autocert manager實(shí)例代碼:
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指定一個(gè)回調(diào)函數(shù)有條件的接受證書(shū)機(jī)構(gòu)CA的TOS服務(wù), 使用AcceptTOS總是接受服務(wù)條款 HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), //HostPolicy用于控制指定哪些域名, 管理器將檢索新證書(shū) Cache: autocert.DirCache("/var/www/.cache"), //緩存證書(shū)和其他狀態(tài) } log.Fatal(autotls.RunWithManager(r, &m)) }
詳情參考autotls包
使用Gin運(yùn)行多個(gè)服務(wù)
可以在主函數(shù)中使用協(xié)程Goroutine運(yùn)行多個(gè)服務(wù), 每個(gè)服務(wù)端口不同, 路由分組也不同. 請(qǐng)參考這個(gè)問(wèn)題, 嘗試運(yùn)行以下示例代碼:
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èn)服務(wù)1: curl http://localhost:8080/ {"code":200,"error":"Welcome server 01"} 模擬訪問(wèn)服務(wù)2: curl http://localhost:8081/ {"code":200,"error":"Welcome server 02"} */
優(yōu)雅的關(guān)閉和重啟服務(wù)
有一些方法可以優(yōu)雅的關(guān)閉或者重啟服務(wù), 比如不應(yīng)該中斷活動(dòng)的連接, 需要優(yōu)雅等待服務(wù)完成后才執(zhí)行關(guān)閉或重啟. 你可以使用第三方包來(lái)實(shí)現(xiàn), 也可以使用內(nèi)置的包自己實(shí)現(xiàn)優(yōu)雅關(guān)閉或重啟.
使用第三方包
fvbock/endless 包, 可以實(shí)現(xiàn)Golang HTTP/HTTPS服務(wù)的零停機(jī)和優(yōu)雅重啟(Golang版本至少1.3以上)
我們可以使用fvbock/endless 替代默認(rèn)的 ListenAndServe方法, 更多詳情, 請(qǐng)參考問(wèn)題#296.
router := gin.Default() router.GET("/", handler) // [...] endless.ListenAndServe(":4242", router)
其他替代包:
manners: 一個(gè)優(yōu)雅的Go HTTP服務(wù), 可以優(yōu)雅的關(guān)閉服務(wù).
graceful: 優(yōu)雅的Go包, 能夠優(yōu)雅的關(guān)閉一個(gè)http.Handler服務(wù)
grace: 該包為Go服務(wù)實(shí)現(xiàn)優(yōu)雅重啟, 零停機(jī)
手動(dòng)實(shí)現(xiàn)
如果你使用Go1.8或者更高的版本, 你可能不需要使用這些庫(kù). 可以考慮使用http.Server的內(nèi)置方法Shutdown()來(lái)優(yōu)雅關(guān)閉服務(wù). 下面的示例描述了基本用法, 更多示例請(qǐng)參考這里
// +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é)程初始化一個(gè)服務(wù), 它不會(huì)阻塞下面的優(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. //等待一個(gè)操作系統(tǒng)的中斷信號(hào), 來(lái)優(yōu)雅的關(guān)閉服務(wù) quit := make(chan os.Signal) // kill (no param) default send syscall.SIGTERM //kill會(huì)發(fā)送終止信號(hào) // kill -2 is syscall.SIGINT //發(fā)送強(qiáng)制進(jìn)程結(jié)束信號(hào) // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it //發(fā)送SIGKILL信號(hào)給進(jìn)程 signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit //阻塞在這里,直到獲取到一個(gè)上面的信號(hào) 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秒鐘的處理超時(shí)時(shí)間 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)雅重啟等知識(shí)點(diǎn)總結(jié)”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識(shí)點(diǎn)總結(jié)這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。