官方中文文档:https://gin-gonic.com/zh-cn/docs/
https://github.com/skyhee/gin-doc-cn
https://www.jianshu.com/p/98965b3ff638/
或者自定义服务器配置
router := gin.Default() s := &http.Server{ Addr: ":8080", Handler: router, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } s.ListenAndServe()基本路由 gin 框架中采用的路由库是 httprouter。
router := gin.Default() //创建带有默认中间件的路由:日志与恢复中间件 //router := gin.New() //创建不带中间件的路由 router.GET("/someGet", getting) router.POST("/somePost", posting) router.PUT("/somePut", putting) router.DELETE("/someDelete", deleting) router.PATCH("/somePatch", patching) router.HEAD("/someHead", head) router.OPTIONS("/someOptions", options)路由组
v1 := router.Group("/v1") //后面路径加在此后面 { v1.POST("/login", loginEndpoint) // /v1/login v1.POST("/submit", submitEndpoint) // /v1/submit v1.POST("/read", readEndpoint) // /v1/read }添加中间件
r := gin.New() // 新建一个没有任何默认中间件的路由 // 全局中间件 // Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。 // By default gin.DefaultWriter = os.Stdout r.Use(gin.Logger()) // Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500。 r.Use(gin.Recovery()) // 你可以为每个路由添加任意数量的中间件。 r.GET("/benchmark", MyBenchLogger(), benchEndpoint) //路由组中间件!在此例中,我们在"authorized"路由组中使用自定义创建的 AuthRequired()中间件 // authorized := r.Group("/", AuthRequired()) // 和使用以下两行代码的效果完全一样: authorized := r.Group("/") authorized.Use(AuthRequired()) { authorized.POST("/login", loginEndpoint) authorized.POST("/submit", submitEndpoint) // 嵌套路由组 testing := authorized.Group("testing") testing.GET("/analytics", analyticsEndpoint) }自定义中间件
func Logger() gin.HandlerFunc { return func(c *gin.Context) { // 在gin上下文中定义变量 c.Set("example", "12345") fmt.Println("开始处理:") //处理请求前 c.Next() //执行r.GET里的func // 处理请求后 status := c.Writer.Status() log.Println(status) //200 } } func main() { r := gin.New() r.Use(Logger()) r.GET("/test", func(c *gin.Context) { example := c.MustGet("example").(string) //获取gin上下文中的变量 log.Println(example) // 会打印: "12345" }) r.Run() } //依次打印:开始处理 // 12345 // 200在中间件及handler中启动新的 Goroutine
不能使用原始的上下文,必须使用只读副本。
r.GET("/long_async", func(c *gin.Context) { // 创建在 goroutine 中使用的副本 cCp := c.Copy() go func() { time.Sleep(5 * time.Second) // 用 time.Sleep() 模拟一个长任务。 log.Println("Done! in path " + cCp.Request.URL.Path) }() })获取GET方法的参数
// url 为 http://localhost:8080/welcome?name=ningskyer时 router.GET("/welcome", func(c *gin.Context) { name := c.DefaultQuery("name", "Guest") //url中未传入name时,name取后面的默认值 lastname := c.Query("lastname") //是c.Request.URL.Query().Get("lastname") 的简写 })获取Post方法的参数
router.POST("/form", func(c *gin.Context) { type := c.DefaultPostForm("type", "alert")//可设置默认值 msg := c.PostForm("msg") })POST请求同时url中携带参数
//curl -d "name=lilei" "http://localhost:8080/?id=5" router.POST("/post", func(c *gin.Context) { id := c.Query("id") name := c.PostForm("name") })使用自定义结构绑定表单数据
GET和POST中均可使用
type StructA struct { FieldA string `form:"field_a"` } type StructB struct { StructA FieldB string `form:"field_b"` } func main() { r := gin.Default() r.GET("/", func (c *gin.Context) { var b StructB c.Bind(&b) //get或post请求中未携带的参数,结构体中保持初始值 fmt.Println(b) }) r.Run() } //浏览器访问:http://localhost:8080/?field_a=hello&field_b=world //打印:{{hello} world}单文件
// 为 multipart forms 设置内存限制 (默认是 32 MiB) // router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { file, _ := c.FormFile("file") c.SaveUploadedFile(file, dst) //将上传的文件存至指定目录 })多文件
router.POST("/upload", func(c *gin.Context) { form, _ := c.MultipartForm() files := form.File["upload[]"] for _, file := range files { c.SaveUploadedFile(file, dst) //将上传的文件存至指定目录 } })写日志文件
//gin.DisableConsoleColor() // 禁用控制台颜色 f, _ := os.Create("gin.log") // 创建记录日志的文件 gin.DefaultWriter = io.MultiWriter(f) // 如果需要将日志同时写入文件和控制台,请使用以下代码 //gin.DefaultWriter = io.MultiWriter(f, os.Stdout) router := gin.Default() router.GET("/ping", func(c *gin.Context) { c.String(200, "pong") })自定义日志格式
router := gin.New() // LoggerWithFormatter 中间件会将日志写入 gin.DefaultWriter // By default gin.DefaultWriter = os.Stdout router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { // 你的自定义格式 return fmt.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, ) })) router.GET("/ping", func(c *gin.Context) { c.String(200, "pong") })templates/index.tmpl
<html> <h1> {{ .title }} </h1> </html>使用不同目录下名称相同的模板
router.LoadHTMLGlob("templates/**/*") router.GET("/posts/index", func(c *gin.Context) { c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{"title": "Posts",}) }) router.GET("/users/index", func(c *gin.Context) { c.HTML(http.StatusOK, "users/index.tmpl", gin.H{"title": "Users",}) })templates/posts/index.tmpl
{{ define "posts/index.tmpl" }} <html><h1> {{ .title }} </h1> <p>Using posts/index.tmpl</p> </html> {{ end }}templates/users/index.tmpl
{{ define "users/index.tmpl" }} <html><h1> {{ .title }} </h1> <p>Using users/index.tmpl</p> </html> {{ end }}使用原生的模板渲染器
import "html/template" html := template.Must(template.ParseFiles("file1", "file2")) router.SetHTMLTemplate(html)自定义分隔符
router.Delims("{[{", "}]}") router.LoadHTMLGlob("/path/to/templates")自定义模板功能
main.go
import ( "fmt" "html/template" "net/http" "time" "github.com/gin-gonic/gin" ) func formatAsDate(t time.Time) string { year, month, day := t.Date() return fmt.Sprintf("%dd/d", year, month, day) } func main() { router := gin.Default() router.Delims("{[{", "}]}") router.SetFuncMap(template.FuncMap{ "formatAsDate": formatAsDate, }) router.LoadHTMLFiles("./testdata/template/raw.tmpl") router.GET("/raw", func(c *gin.Context) { c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), }) }) router.Run() }raw.tmpl
使用了管道
Date: {[{.now | formatAsDate}]}结果:
Date: 2019/06/01将模板写入二进制文件
你可以使用go-assets将服务器构建成一个包含模板的二进制文件
func main() { r := gin.New() t, err := loadTemplate() if err != nil { panic(err) } r.SetHTMLTemplate(t) r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "/html/index.tmpl",nil) }) r.Run(":8080") } func loadTemplate() (*template.Template, error) { t := template.New("") for name, file := range Assets.Files { if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { continue } h, err := ioutil.ReadAll(file) if err != nil { return nil, err } t, err = t.New(name).Parse(string(h)) if err != nil { return nil, err } } return t, nil }参见examples/assets-in-binary目录中的例子
HTTP 重定向很容易。 内部、外部重定向均支持。
r.GET("/test", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "http://www.google.com/") })路由重定向,使用 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"}) })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{"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{"error": "Welcome server 01",},) }) 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 { return server01.ListenAndServe() }) g.Go(func() error { return server02.ListenAndServe() }) if err := g.Wait(); err != nil { log.Fatal(err) } }