gin-contrib/cors、Kafka服务 等
发布时间:2025-06-24 19:34:40 作者:北方职教升学中心 阅读量:081
加载模板,启动 HTTP 服务器:./main.go
packagemainimport("github.com/gin-gonic/gin""net/http")funcmain(){router :=gin.Default()router.LoadHTMLGlob("./templates/*.html")// 把 "./templates/" 目录下的所有 html 文件加载为模板,对应的模板名称为文件名// router.LoadHTMLFiles("./templates/hello.html") // 加载单个 HTML 文件模板// GET /hellorouter.GET("/hello",func(ctx *gin.Context){// 渲染名称为 "hello.html" 的已加载模板,并向模板中传入一个对象ctx.HTML(http.StatusOK,"hello.html",gin.H{"name":"tom","age":18,})})err :=router.Run(":8080")iferr !=nil{panic(err)}}
测试访问:
$ curlhttp://localhost:8080/hello<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Hello</title></head><body>My name is tom, age is 18.</body></html>
14.2 自定义模板名称
LoadHTMLGlob()
和 LoadHTMLFiles()
加载的 HTML 模板,默认使用文件名作为模板名称。
packagemainimport("context""errors""github.com/gin-gonic/gin""log""net/http""os""os/signal""time")funcmain(){router :=gin.Default()router.GET("/ping",func(ctx *gin.Context){ctx.String(http.StatusOK,"pong")})server :=&http.Server{Addr:":8080",Handler:router,}// 异步启动服务gofunc(){err :=server.ListenAndServe()iferr !=nil&&!errors.Is(err,http.ErrServerClosed){log.Fatalf("listen error: %+vn",err)}}()// 监听中断信号quit :=make(chanos.Signal,1)signal.Notify(quit,os.Interrupt)<-quit log.Println("Interrupt signal received.")// 收到中断信号后,主动关闭服务(设置5秒超时时间,超过5秒没有正常关闭服务,则取消上下文)ctx,cancel :=context.WithTimeout(context.Background(),5*time.Second)defercancel()// 如果顺利关闭服务,则提前取消掉上下文err :=server.Shutdown(ctx)// 主动关闭服务,关闭服务前有5秒钟时间做收尾工作(例如关闭其他关联的服务,把缓存的日志刷到磁盘)iferr !=nil{log.Fatalf("Server Shutdown Error: %+vn",err)}log.Println("Server Shutdown.")}
启动服务后,按 Ctrl + C 向进程发出中断信号。
15.3 运行多个服务
一个进程可以运行多个服务,包括 HTTP服务、
管理多个服务,可以使用 golang.org/x/sync/errgroup 或 sync.WaitGroup。
ctx.GetPostFormArray(key string) (values []string, ok bool)
:以数组的方式获取表单值。设置/获取 Cookie 方法:
ctx.SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
:设置 Cookie 到响应头。8.1 自定义中间件
packagemainimport("fmt""github.com/gin-gonic/gin""net/http")// Middleware1 返回一个中间件函数funcMiddleware1()gin.HandlerFunc {returnfunc(ctx *gin.Context){fmt.Println("Middleware1 Start")ctx.Set("name","tom")// 在 Context 中保存变量ctx.Next()// 调用下一个中间件,如果没有更多中间件,则调用 handlerfmt.Println("Middleware1 End")}}// Middleware2 一个中间件函数funcMiddleware2(ctx *gin.Context){name,_:=ctx.Get("name")// 获取 Context 中保存的变量fmt.Println("Middleware2 Start, name:",name)ctx.Next()// 调用下一个中间件,如果没有更多中间件,则调用 handlerfmt.Println("Middleware2 End")}funcmain(){router :=gin.Default()// 注册中间件,多个中间件将依次调用router.Use(Middleware1())router.Use(Middleware2)router.GET("/ping",func(ctx *gin.Context){name,_:=ctx.Get("name")fmt.Println("GET Handler: name:",name)ctx.String(http.StatusOK,"pong")})err :=router.Run(":8080")iferr !=nil{panic(err)}}
访问
http://localhost:8080/ping
,输出日志:Middleware1 StartMiddleware2 Start, name: tomGET Handler: name: tomMiddleware2 EndMiddleware1 End
8.2 日志中间件
packagemainimport("fmt""github.com/gin-gonic/gin""net/http""time")funcLogFormatter(param gin.LogFormatterParams)string{// 自定义日志格式returnfmt.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,)}funcmain(){router :=gin.Default()// gin.LoggerWithFormatter 中间件会写入日志到 gin.DefaultWriter// 默认 gin.DefaultWriter = os.Stdoutrouter.Use(gin.LoggerWithFormatter(LogFormatter))router.GET("/ping",func(ctx *gin.Context){ctx.String(http.StatusOK,"pong")})err :=router.Run(":8080")iferr !=nil{panic(err)}}
8.3 BasicAuth 中间件
packagemainimport("github.com/gin-gonic/gin""net/http")funcmain(){router :=gin.Default()// 全局注册 BasicAuth 中间件 (也可以在某个 path 注册, 或某个路由组注册)router.Use(gin.BasicAuth(gin.Accounts{"user1":"pass1",// 账号 {用户名: 密码}"user2":"pass2",}))router.GET("/user",func(ctx *gin.Context){// 获取通过 BasicAuth 中间件认证的用户名,在 gin.BasicAuth 中通过 ctx.Set(gin.AuthUserKey, user) 设置user :=ctx.MustGet(gin.AuthUserKey).(string)ctx.String(http.StatusOK,"hello %s",user)})err :=router.Run(":8080")iferr !=nil{panic(err)}}
测试访问:
curl-v"http://user1:pass1@localhost:8080/user"
8.4 Gzip 中间件
Gin 官方的 GitHub 中提供了许多中间件,例如:gin-contrib/gzip、默认文件名为 HTML 文件模板的名称。router:=gin.Default()type Person struct {Name string `json:"name"`Age int `json:"age"`}// GET /get_json, 相当于: r.Handle("GET", path, handlers)router.GET("/get_json",func(ctx *gin.Context){p:=&Person{Name:"tom",Age:18,}// 将给定的结构体序列化为 JSON 写入响应 Body 中,并设置响应头 "Content-Type: application/json; charset=utf-8"ctx.JSON(http.StatusOK,p)// ctx.AsciiJSON(code int, obj any) // 非 ASCII 字符以 \u 的形式表示// 其他自动序列化对象响应的方法// ctx.YAML(code int, obj any) // Content-Type: application/yaml; charset=utf-8// ctx.TOML(code int, obj any) // Content-Type: application/toml; charset=utf-8// ctx.XML(code int, obj any) // Content-Type: application/xml; charset=utf-8// ctx.ProtoBuf(http.StatusOK, resBody) // Content-Type: application/x-protobuf})err:=router.Run(":8080")iferr !=nil {panic(err)}}
2.2 通用响应数据
ctx.String()
方法用于将字符串写入到 Body,并设置Content-Type: text/plain; charset=utf-8
响应头。errgroup.Group
内部使用sync.WaitGroup
实现,但增加了对返回错误的任务的处理。gin-contrib/i18n 等,具体参考 gin-contrib — GitHub。// 当传递给 group.Go() 的函数第一次返回非 nil error 或 第一次 group.Wait() 返回时(以先发生者为准),返回的 ctx 会被取消group,ctx :=errgroup.WithContext(ctx)// HTTP 服务router1 :=gin.Default()router1.GET("/ping",func(ctx *gin.Context){ctx.String(http.StatusOK,"pong")})server1 :=&http.Server{Addr:":8080",Handler:router1,}// 在新的 goroutine 中启动服务group.Go(func()error{log.Println("Server1 Start.")returnserver1.ListenAndServe()})// HTTP 服务router2 :=gin.Default()router2.GET("/ping",func(ctx *gin.Context){ctx.String(http.StatusOK,"pong")})server2 :=&http.Server{Addr:":8081",Handler:router1,}group.Go(func()error{log.Println("Server2 Start.")returnserver2.ListenAndServe()})// 异步接收中断信号的服务group.Go(func()error{quit :=make(chanos.Signal,1)signal.Notify(quit,os.Interrupt)<-quit log.Println("Interrupt Signal Received.")// 收到中断信号后,主动关闭服务(超时5秒则强制取消上下文)ctx,cancel :=context.WithTimeout(context.Background(),5*time.Second)defercancel()// 主动关闭 HTTP 服务iferr :=server1.Shutdown(ctx);err !=nil{log.Printf("Shutdown Server1 Error: %+vn",err)}else{log.Println("Server1 Shutdown.")}// 主动关闭 HTTP 服务iferr :=server2.Shutdown(ctx);err !=nil{log.Printf("Shutdown Server2 Error: %+vn",err)}else{log.Println("Server2 Shutdown.")}log.Println("All Server Safely Exited.")returnnil})// 等待组内所有 goroutine 结束(如果其中一个 goroutine 返回了 err,则会取消 ctx),// 返回的 err 是这一组 goroutine 中第一个返回的非 nil erroriferr :=group.Wait();err !=nil&&!errors.Is(err,http.ErrServerClosed){log.Fatalln(err)}// group.Wait() 内部也是通过调用 sync.WaitGroup 的 Wait() 方法实现的等待log.Println("APP EXISTS.")}