溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Golang GinWeb框架之重定向/自定義中間件/認(rèn)證/HTTPS支持/優(yōu)雅重啟等知識(shí)點(diǎn)總結(jié)

發(fā)布時(shí)間:2021-10-22 16:42:34 來(lái)源:億速云 閱讀:298 作者:iii 欄目:web開(kāi)發(fā)

這篇文章主要講解了“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)注!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI