Gin开发01

来源:互联网 发布:网络打假教程 编辑:程序博客网 时间:2024/06/09 16:50

Gin学习

1 基础环境

首先配置好go环境,设置好GOPATH环境变量。我的为例,输入go env,输出如下:

C:\Users\Xin>go envset GOARCH=386set GOBIN=C:\go\bin  // 必须设置set GOEXE=.exeset GOHOSTARCH=386set GOHOSTOS=windowsset GOOS=windowsset GOPATH=D:\go    // 必须设置set GORACE=set GOROOT=C:\go    // 必须设置set GOTOOLDIR=C:\go\pkg\tool\windows_386set GCCGO=gccgoset GO386=set CC=gccset GOGCCFLAGS=-m32 -mthreads -fmessage-length=0set CXX=g++set CGO_ENABLED=1set PKG_CONFIG=pkg-configset CGO_CFLAGS=-g -O2set CGO_CPPFLAGS=set CGO_CXXFLAGS=-g -O2set CGO_FFLAGS=-g -O2set CGO_LDFLAGS=-g -O2

然后,使用go下载gin库,go get github.com/gin-gonic/gin,一般使用需要的依赖:

import "github.com/gin-gonic/gin"import "net/http"

2 Demo和学习记录

2.1 最简单的Demo

首先官方最简单的一个Demo:

// 文件名为 t1.gopackage mainimport "github.com/gin-gonic/gin"func main() {    r := gin.Default()    r.GET("/ping", func(c *gin.Context) {        c.JSON(200, gin.H{            "message": "pong",        })    })    r.Run() // listen and serve on 0.0.0.0:8080}
  • %GOPATH%\src文件夹中创建一个自己的项目文件夹,比如 Demo1,然后新建一个go文件,如t1.go。将上述代码复制进入,保存。
  • 新建CMD窗口,进入Demo1所在文件夹,然后输入go run t1.go将我们的代码运行起来。
  • 在浏览器中输入http://127.0.0.1:8080/ping,浏览器如果有回复,则说明我们代码成功!

2.2 路由与参数获取

路由的几种方式:
- 绝对匹配。对/xxx的GET/POST/DELETE/HEAD…请求的路由。

    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)    route.Any("/testing", startPage)
  • /user/:name 对二级名称关联name变量。
  • /user/:name/*action与上一个的区别是*部分可以有,也可以没有,即可匹配/user/:name/url。

现代化路由形式:
分组的路由:

func main() {    router := gin.Default()    // Simple group: v1    v1 := router.Group("/v1")    {        v1.POST("/login", loginEndpoint)        v1.POST("/submit", submitEndpoint)        v1.POST("/read", readEndpoint)    }    // Simple group: v2    v2 := router.Group("/v2")    {        v2.POST("/login", loginEndpoint)        v2.POST("/submit", submitEndpoint)        v2.POST("/read", readEndpoint)    }    router.Run(":8080")}

参数获取:
GET请求的参数获取:

func main() {    router := gin.Default()    // url matching:  /welcome?firstname=Jane&lastname=Doe    router.GET("/welcome", func(c *gin.Context) {        firstname := c.DefaultQuery("firstname", "Guest") // 带有默认返回值的获取方式        lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname"), 可能为nil        c.String(http.StatusOK, "Hello %s %s", firstname, lastname)    })    router.Run(":8080")}

Multipart/UrlEncode表单:

func main() {    router := gin.Default()    router.POST("/form_post", func(c *gin.Context) {        message := c.PostForm("message")        nick := c.DefaultPostForm("nick", "anonymous")        c.JSON(200, gin.H{            "status":  "posted",            "message": message,            "nick":    nick,        })    })    router.Run(":8080")}

当两种方式都存在时,可以混合使用。

文件上传:
单个文件:

func main() {    router := gin.Default()    router.POST("/upload", func(c *gin.Context) {        // single file        file, _ := c.FormFile("file")        log.Println(file.Filename)        // Upload the file to specific dst.        // c.SaveUploadedFile(file, dst)               c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))    })    router.Run(":8080")}

多个文件:

func main() {    router := gin.Default()    router.POST("/upload", func(c *gin.Context) {        // Multipart form        form, _ := c.MultipartForm()        files := form.File["upload[]"]        for _, file := range files {            log.Println(file.Filename)            // Upload the file to specific dst.            // c.SaveUploadedFile(file, dst)               }        c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))    })    router.Run(":8080")}

2.3 中间件

在上面的代码中,我们创建gin的实例使用的是r := gin.Default()里面开启了很多默认的中间件。如果你想使用一个干净的,没有任何中间件的gin实例,可以使用r := gin.New()

func main() {    // Creates a router without any middleware by default    r := gin.New()    // Global middleware    r.Use(gin.Logger())    r.Use(gin.Recovery())    // Per route middleware, you can add as many as you desire.    r.GET("/benchmark", MyBenchLogger(), benchEndpoint)    // Authorization group    // authorized := r.Group("/", AuthRequired())    // exactly the same as:    authorized := r.Group("/")    // per group middleware! in this case we use the custom created    // AuthRequired() middleware just in the "authorized" group.    authorized.Use(AuthRequired())    {        authorized.POST("/login", loginEndpoint)        authorized.POST("/submit", submitEndpoint)        authorized.POST("/read", readEndpoint)        // nested group        testing := authorized.Group("testing")        testing.GET("/analytics", analyticsEndpoint)    }    // Listen and serve on 0.0.0.0:8080    r.Run(":8080")}

2.4 数据绑定与验证

Gin支持绑定的数据格式为Json, XML, Url Param(foo=bar&boo=baz)。数据验证使用的是go-playground/validator.v8

// Binding from JSONtype Login struct {    User     string `form:"user" json:"user" binding:"required"`    Password string `form:"password" json:"password" binding:"required"`}func main() {    router := gin.Default()    // Example for binding JSON ({"user": "manu", "password": "123"})    router.POST("/loginJSON", func(c *gin.Context) {        var json Login        if c.BindJSON(&json) == nil {            if json.User == "manu" && json.Password == "123" {                c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})            } else {                c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})            }        }    })    // Example for binding a HTML form (user=manu&password=123)    router.POST("/loginForm", func(c *gin.Context) {        var form Login        // This will infer what binder to use depending on the content-type header.        if c.Bind(&form) == nil {            if form.User == "manu" && form.Password == "123" {                c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})            } else {                c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})            }        }    })    // Listen and serve on 0.0.0.0:8080    router.Run(":8080")}

如果只绑定URL的字段,可以使用如下:

if c.BindQuery(&person) == nil {    log.Println("====== Only Bind By Query String ======")    log.Println(person.Name)    log.Println(person.Address)}

如果POST和URL数据都需要:

// If `GET`, only `Form` binding engine (`query`) used.// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48if c.Bind(&person) == nil {    log.Println(person.Name)    log.Println(person.Address)}

绑定HTML中的checkboxes:

// HTML部分 <form action="/" method="POST">    <p>Check some colors</p>    <label for="red">Red</label>    <input type="checkbox" name="colors[]" value="red" id="red" />    <label for="green">Green</label>    <input type="checkbox" name="colors[]" value="green" id="green" />    <label for="blue">Blue</label>    <input type="checkbox" name="colors[]" value="blue" id="blue" />    <input type="submit" /></form>// go语言部分  type myForm struct {    Colors []string `form:"colors[]"`}...func formHandler(c *gin.Context) {    var fakeForm myForm    c.Bind(&fakeForm)    c.JSON(200, gin.H{"color": fakeForm.Colors})}

Multipart/Urlencoded的绑定:

type LoginForm struct {    User     string `form:"user" binding:"required"`    Password string `form:"password" binding:"required"`}func main() {    router := gin.Default()    router.POST("/login", func(c *gin.Context) {        // you can bind multipart form with explicit binding declaration:        // c.MustBindWith(&form, binding.Form)        // or you can simply use autobinding with Bind method:        var form LoginForm        // in this case proper binding will be automatically selected        if c.Bind(&form) == nil {            if form.User == "user" && form.Password == "password" {                c.JSON(200, gin.H{"status": "you are logged in"})            } else {                c.JSON(401, gin.H{"status": "unauthorized"})            }        }    })    router.Run(":8080")}

2.5 数据渲染

XML/JSON/YAML类型数据:

func main() {    r := gin.Default()    // gin.H is a shortcut for map[string]interface{}    r.GET("/someJSON", func(c *gin.Context) {        c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})    })    r.GET("/moreJSON", func(c *gin.Context) {        // You also can use a struct        var msg struct {            Name    string `json:"user"`            Message string            Number  int        }        msg.Name = "Lena"        msg.Message = "hey"        msg.Number = 123        // Note that msg.Name becomes "user" in the JSON        // Will output  :   {"user": "Lena", "Message": "hey", "Number": 123}        c.JSON(http.StatusOK, msg)    })    r.GET("/someXML", func(c *gin.Context) {        c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})    })    r.GET("/someYAML", func(c *gin.Context) {        c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})    })    // Listen and serve on 0.0.0.0:8080    r.Run(":8080")}

SecureJSON:(原始JSON加前缀)

func main() {    r := gin.Default()    // You can also use your own secure json prefix    r.SecureJsonPrefix("myTitle") // 默认是while(1);    r.GET("/someJSON", func(c *gin.Context) {        names := []string{"lena", "austin", "foo"}        // Will output  :   while(1);["lena","austin","foo"]        c.SecureJSON(http.StatusOK, names)    })    // Listen and serve on 0.0.0.0:8080    r.Run(":8080")}

静态文件渲染:

func main() {    router := gin.Default()    router.Static("/assets", "./assets")    router.StaticFS("/more_static", http.Dir("my_file_system"))    router.StaticFile("/favicon.ico", "./resources/favicon.ico")    // Listen and serve on 0.0.0.0:8080    router.Run(":8080")}

HTML页面渲染:

func main() {    router := gin.Default()    router.LoadHTMLGlob("templates/*")    //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")    router.GET("/index", func(c *gin.Context) {        c.HTML(http.StatusOK, "index.tmpl", gin.H{            "title": "Main website",        })    })    router.Run(":8080")}

2.6 自定义

自定义模版渲染:

import "html/template"func main() {    router := gin.Default()    html := template.Must(template.ParseFiles("file1", "file2"))    router.SetHTMLTemplate(html)    router.Run(":8080")}

自定义分隔符:

r := gin.Default()r.Delims("{[{", "}]}")r.LoadHTMLGlob("/path/to/templates"))

自定义模版函数:

func formatAsDate(t time.Time) string {    year, month, day := t.Date()    return fmt.Sprintf("%d%02d/%02d", year, month, day)}func main() {    router := gin.Default()    router.Delims("{[{", "}]}")    router.SetFuncMap(template.FuncMap{        "formatAsDate": formatAsDate,    })    router.LoadHTMLFiles("./fixtures/basic/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(":8080")}// taw.tmplDate: {[{.now | formatAsDate}]}

重定向:

r.GET("/test", func(c *gin.Context) {    c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")})

2.7 自定义中间件

func Logger() gin.HandlerFunc {    return func(c *gin.Context) {        t := time.Now()        // Set example variable        c.Set("example", "12345")        // before request        c.Next()        // after request        latency := time.Since(t)        log.Print(latency)        // access the status we are sending        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)        // it would print: "12345"        log.Println(example)    })    // Listen and serve on 0.0.0.0:8080    r.Run(":8080")}

在定制中间件时,不要直接使用原始context,而要使用一个read-only的copy。

授权验证中间件:

// simulate some private datavar 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.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        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")}

2.8 定制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()}

2.10 支持加密HTTPS

最简单的方式:

package mainimport (    "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")    })    log.Fatal(autotls.Run(r, "example1.com", "example2.com"))}

自己管理证书的方式:

package mainimport (    "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,        HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),        Cache:      autocert.DirCache("/var/www/.cache"),    }    log.Fatal(autotls.RunWithManager(r, &m))}

2.11 优雅地关闭服务

// +build go1.8package mainimport (    "context"    "log"    "net/http"    "os"    "os/signal"    "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,    }    go func() {        // service connections        if err := srv.ListenAndServe(); err != nil {            log.Printf("listen: %s\n", err)        }    }()    // Wait for interrupt signal to gracefully shutdown the server with    // a timeout of 5 seconds.    quit := make(chan os.Signal)    signal.Notify(quit, os.Interrupt)    <-quit    log.Println("Shutdown Server ...")    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)    defer cancel()    if err := srv.Shutdown(ctx); err != nil {        log.Fatal("Server Shutdown:", err)    }    log.Println("Server exist")}