引言本文跟随以上(GolangGinWebFramework7-静态文件/模板渲染)继续探索GinWeb框架。重定向Gin以返回HTTP重定向非常简单,只需使用Redirect方法即可。内外链都支持.packagemainimport("github.com/gin-gonic/gin""net/http")funcmain(){r:=gin.Default()r.GET("/test",func(c*gin.Context){c.Redirect(http.StatusMovedPermanently,"http://www.google.com/")//重定向到外部链接})//重定向到内部链接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":"ThisisHomepage"})})r.Run(":8080")}/*重定向到外部链接,访问:http://localhost:8080/test重定向到内部链接,访问:http://localhost:8080/internal*/POST方法的HTTP重定向,参考问题#444https://github.com/gin-gonic/gin/issues/444r.POST("/test",func(c*gin.Context){c.Redirect(http.StatusFound,"/foo")})如果要生成路由重定向,类似于上面的内部重定向,使用HandleContext方法,这样使用: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"})})自定义中间件包mainimport("github.com/gin-gonic/gin""log""time")//自定义日志中间件funcLogger()gin.HandlerFunc{returnfunc(c*gin.Context){t:=time.Now()//Setexamplevariablesetsgin上下文中的键值对c.Set("example","12345")//beforerequest//next方法只能在中间件中使用,在当前中间件中,从方法链中暂停执行处理器c.Next()//afterrequest打印中间件执行耗时latency:=time.Since(t)log.Print(latency)//accessthestatuswearesending打印这个中间件的状态码status:=c.Writer.Status()log.Println(status)}}funcmain(){r:=gin.New()//使用这个自定义中间件r.Use(Logger())r.GET("/test",func(c*gin.Context){example:=c.MustGet("example").(string)//从上下文中获取键值对//itwouldprint:"12345"log.Println(example)})//Listenandserveon0.0.0.0:8080r.Run(":8080")}使用basicauthenticationBasicAuth()middlewarepackagemainimport("github.com/gin-gonic/gin""net/http")//模拟一些privatedatavarsecrets=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"},}funcmain(){r:=gin.Default()//Groupusinggin.BasicAuth()middleware//gin.Accountsisasashorcutformap[string]string//授权的路由组使用基本认证中间件,参数是gin.Accounts,是一个map,key名是用户名,key值为密码,中间件会将认证信息保存在授权的cookie中:=r.Group("/admin",gin.BasicAuth(gin.Accounts{"foo":"bar","austin":"1234","lena":"hello2","manu":"4321",}))///admin/secretsendpoint//hit"localhost:8080/admin/secretsauthorized.GET("/secrets",func(c*gin.Context){//getuser,itwassetbytheBasicAuthmiddleware//从cookie中获取用户认证信息,key名为useruser:=c.MustGet(gin.AuthUserKey).(string)ifsecret,ok:=secrets[user];ok{c.JSON(http.StatusOK,gin.H{"user":user,"secret":secret})}else{c.JSON(http.StatusOK,gin.H{"user":user,"secret":"NOSECRET:("})}})//Listenandserveon0.0.0.0:8080r.Run(":8080")}/*测试访问:http://localhost:8080/admin/secrets*/在中间件中使用协程Goroutines时,在中间件或控制器中启动一个新的协程时,不能直接使用原来的Gin上下文,必须使用只读上下文拷贝packagemainimport("github.com/gin-gonic/gin""log""time")funcmain(){r:=gin.Default()r.GET("/long_async",func(c*gin.Context){//createcopytobeusedinsidethegoroutine//创建一个副本Gin上下文,准备在协程Goroutine中使用cCp:=c.Copy()gofunc(){//simulatealongtaskwithtime.Sleep().5seconds//模拟长任务,这里是5秒time.Sleep(5*time.Second)//注意你使用的是复制的上下文“cCp”,重要//在中间件或控制器中启动协程时,不能直接使用原始上下文,on的只读副本行上下文必须使用log.Println("Done!inpath"+cCp.Request.URL.Path)}()})r.GET("/long_sync",func(c*gin.Context){//simulatealongtaskwithtime.Sleep().5ssecondstime.Sleep(5*time.Second)//因为我们没有使用agoroutine,wedonothavetocopythecontext//没有使用协程时,可以直接使用Gin上下文log.Println("Done!inpath"+c.Request.URL.Path)})//Listenandserveon0.0.0.0:8080r.Run(":8080")}/*模拟同步阻塞访问:http://localhost:8080/long_sync模拟异步非阻塞访问:http://localhost:8080/long_async*/from直接使用http.ListenAndServe()方法定义HTTP配置:funcmain(){router:=gin.Default()http.ListenAndServe(":8080",router)}或者自定义HTTP配置funcmain(){路由器:=杜松子酒。Default()s:=&http.Server{Addr:":8080",Handler:router,ReadTimeout:10*time.Second,WriteTimeout:10*time.Second,MaxHeaderBytes:1<<20,}s.ListenAndServe()}支持Let'sEncrypt证书加密处理HTTPS下面是一行LetsEncryptHTTPS服务包mainimport("log""github.com/gin-gonic/autotls""github.com/gin-gonic/gin")funcmain(){r:=gin.Default()//Pinghandlerr.GET("/ping",func(c*gin.Context){c.String(200,"pong")})//一行LetsEncrypt证书,处理httpslog.Fatal(autotls.Run(r,"example1.com","example2.com"))}自定义自动证书管理器autocert管理器示例代码:packagemainimport("log""github.com/gin-gonic/autotls""github.com/gin-gonic/gin""golang.org/x/crypto/acme/autocert")funcmain(){r:=gin.Default()//Pinghandlerr.GET("/ping",func(c*gin.Context){c.String(200,"pong")})m:=autocert.Manager{Prompt:autocert.AcceptTOS,//Prompt指定回调函数有条件地接受证书颁发机构CA的TOS服务,使用AcceptTOS始终接受服务条款HostPolicy:autocert.HostWhitelist("example1.com","example2.com"),//HostPolicy用于控制指定哪些域名,管理器会取回新的证书Cache:autocert.DirCache("/var/www/.cache"),//缓存证书等状态}log.Fatal(autotls.RunWithManager(r,&m))}详见autotls包使用Gin运行多个服务可以使用协程Goroutine运行多个主要功能中的服务,每个服务副端口不同,路由分组也不同。请参考这个问题,尝试运行以下示例代码:packagemainimport("log""net/http""time""github.com/gin-gonic/gin""golang.org/x/sync/errgroup")var(gerrgroup.Group)funcrouter01()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":"Welcomeserver01",},)})return}funcrouter02()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":"Welcomeserver02",},)})returne}funcmain(){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()iferr!=nil&&err!=http.ErrServerClosed{log.Fatal(err)}returnerr})g.Go(func()error{err:=server02.ListenAndServe()iferr!=nil&&err!=http.ErrServerClosed{log.Fatal(err)}returnerr})iferr:=g.Wait();err!=nil{log.Fatal(err)}}/*模拟访问服务1:curlhttp://localhost:8080/{"code":200,"error":"Welcomeserver01"}模拟访问服务2:curlhttp://localhost:8081/{"code":200,"error":"Welcomeserver02"}*/Gracefulshutdownandrestartofservices有一些方法可以优雅地关闭或重启服务,比如不应该中断活动连接,需要优雅地等待服务完成后再关闭或重启。可以使用第三方包来实现,也可以使用内置包自行实现优雅关机或重启。使用第三方包fvbock/endless包,可以实现GolangHTTP/HTTPS服务(Golang1.3以上版本)零宕机和优雅重启。我们可以使用fvbock/endless来替换默认的ListenAndServe方法,更多细节请参考issue#296.router:=gin.Default()router.GET("/",handler)//[...]endless.ListenAndServe(":4242",router)其他替代包:manners:一个优雅的GoHTTP服务,可以优雅的关闭服务。graceful:一个优雅的Go包,可以优雅地关闭一个http.Handler服务。grace:这个包实现了Go服务的优雅重启,零宕机手动实现如果你使用Go1.8或更高版本,你可能不需要使用这些库。可以考虑使用http.Server的内置方法Shutdown()来优雅的关闭服务。下面的例子描述了基本的用法,更多的例子可以参考这里//+buildgo1.8packagemainimport("context""log""net/http""os""os/signal""syscall""time""github.com/gin-gonic/gin")funcmain(){router:=gin.Default()router.GET("/",func(c*gin.Context){time.Sleep(5*time.Second)c.String(http.StatusOK,"WelcomeGinServer")})srv:=&http.Server{Addr:":8080",Handler:router,}//在goroutine中初始化服务器,这样//它不会阻塞下面的优雅关闭处理//用协程初始化一个服务,它会不阻塞下面优雅的逻辑处理gofunc(){iferr:=srv.ListenAndServe();err!=nil&&err!=http.ErrServerClosed{log.Fatalf("listen:%s\n",err)}}()//Waitforinterruptsignaltogracefullyshutdowntheserverwith//atimeoutof5seconds.//等待操作系统中断信号优雅关闭服务2issyscall.SIGINT//发送一个强制进程结束信号//kill-9issyscall.SIGKILLbutcan'tbecatch,sodon'tneedaddit//发送SIGKILL信号给进程signal.Notify(quit,syscall.SIGINT,syscall.SIGTERM)<-quit//在这里阻塞,直到上述信号之一出现ainedlog.Println("Shuttingdownserver...")//Thecontextisusedtoinformtheserverithas5secondstofinish//therequestitiscurrentlyhandling//这里使用context上下文包,有一个5秒的处理超时ctx,cancel:=context.WithTimeout(context.Background(),5*time.Second)defercancel()iferr:=srv.Shutdown(ctx);err!=nil{//使用内置的Shutdown方法优雅地关闭服务
