Gin-Web-Framework官方指南中文(下篇)

  • 2019 年 10 月 31 日
  • 笔记

案例用法讲解

1、使用Ascii JSON生成带有转义的非ASCII字符的纯ASCII JSON。

func main() {  	r := gin.Default()    	r.GET("/someJSON", func(c *gin.Context) {  		data := map[string]interface{}{  			"lang": "GO语言",  			"tag":  "<br>",  		}    		// will output : {"lang":"GOu8bedu8a00","tag":"u003cbru003e"}  		c.AsciiJSON(http.StatusOK, data)  	})    	// Listen and serve on 0.0.0.0:8080  	r.Run(":8080")  }

2、利用结构体绑定表单数据

type StructA struct {      FieldA string `form:"field_a"`  }    type StructB struct {      NestedStruct StructA      FieldB string `form:"field_b"`  }    type StructC struct {      NestedStructPointer *StructA      FieldC string `form:"field_c"`  }    type StructD struct {      NestedAnonyStruct struct {          FieldX string `form:"field_x"`      }      FieldD string `form:"field_d"`  }    func GetDataB(c *gin.Context) {      var b StructB      c.Bind(&b)      c.JSON(200, gin.H{          "a": b.NestedStruct,          "b": b.FieldB,      })  }    func GetDataC(c *gin.Context) {      var b StructC      c.Bind(&b)      c.JSON(200, gin.H{          "a": b.NestedStructPointer,          "c": b.FieldC,      })  }    func GetDataD(c *gin.Context) {      var b StructD      c.Bind(&b)      c.JSON(200, gin.H{          "x": b.NestedAnonyStruct,          "d": b.FieldD,      })  }    func main() {      r := gin.Default()      r.GET("/getb", GetDataB)      r.GET("/getc", GetDataC)      r.GET("/getd", GetDataD)        r.Run()  }

使用curl简单测试或者浏览器都可以

$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"  {"a":{"FieldA":"hello"},"b":"world"}  $ curl "http://localhost:8080/getc?field_a=hello&field_c=world"  {"a":{"FieldA":"hello"},"c":"world"}  $ curl "http://localhost:8080/getd?field_x=hello&field_d=world"  {"d":"world","x":{"FieldX":"hello"}}

3、绑定HTML审核表单

伪代码

package main    import (      "github.com/gin-gonic/gin"  )    type myForm struct {      Colors []string `form:"colors[]"`  }    func main() {      r := gin.Default()        r.LoadHTMLGlob("views/*")      r.GET("/", indexHandler)      r.POST("/", formHandler)        r.Run(":8080")  }    func indexHandler(c *gin.Context) {      c.HTML(200, "form.html", nil)  }    func formHandler(c *gin.Context) {      var fakeForm myForm      c.Bind(&fakeForm)      c.JSON(200, gin.H{"color": fakeForm.Colors})  }

伪代码form表单HTML—-"views/form.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>

返回结果

{"color":["red","green","blue"]}  

4、绑定查询字符串或者post数据

package main    import "log"  import "github.com/gin-gonic/gin"    type Person struct {  	Name    string `form:"name" json:"name"`  	Address string `form:"address" json:"address"`  }    func main() {  	route := gin.Default()  	route.GET("/testing", startPage)  	route.Run(":8085")  }    func startPage(c *gin.Context) {  	var person Person  	if c.Bind(&person) == nil {  		log.Println("====== Bind By Query String ======")  		log.Println(person.Name)  		log.Println(person.Address)  	}    	if c.BindJSON(&person) == nil {  		log.Println("====== Bind By JSON ======")  		log.Println(person.Name)  		log.Println(person.Address)  	}    	c.String(200, "Success")  }
# bind by query  $ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz"  # bind by json  $ curl -X GET localhost:8085/testing --data '{"name":"JJ", "address":"xyz"}' -H "Content-Type:application/json"  

5、绑定URI

package main    import "github.com/gin-gonic/gin"    type Person struct {  	ID string `uri:"id" binding:"required,uuid"`  	Name string `uri:"name" binding:"required"`  }    func main() {  	route := gin.Default()  	route.GET("/:name/:id", func(c *gin.Context) {  		var person Person  		if err := c.ShouldBindUri(&person); err != nil {  			c.JSON(400, gin.H{"msg": err})  			return  		}  		c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})  	})  	route.Run(":8088")  }
$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3  $ curl -v localhost:8088/thinkerou/not-uuid

6、使用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")  }    // loadTemplate loads templates embedded by go-assets-builder  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  }

7、控制日志输出颜色

func main() {      // Disable log's color      gin.DisableConsoleColor()        // Creates a gin router with default middleware:      // logger and recovery (crash-free) middleware      router := gin.Default()        router.GET("/ping", func(c *gin.Context) {          c.String(200, "pong")      })        router.Run(":8080")  }
func main() {      // Force log's color      gin.ForceConsoleColor()        // Creates a gin router with default middleware:      // logger and recovery (crash-free) middleware      router := gin.Default()        router.GET("/ping", func(c *gin.Context) {          c.String(200, "pong")      })        router.Run(":8080")  }  

8、使用HTTP配置

直接使用http.ListenAndServe()

func main() {  	router := gin.Default()  	http.ListenAndServe(":8080", router)  }

或者自己定义参数

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()  }

9、定制log文件

func main() {  	router := gin.New()  	// LoggerWithFormatter middleware will write the logs to gin.DefaultWriter  	// By default gin.DefaultWriter = os.Stdout  	router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {  		// your custom format  		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.Use(gin.Recovery())  	router.GET("/ping", func(c *gin.Context) {  		c.String(200, "pong")  	})  	router.Run(":8080")  }

输出的例子:

::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "

10、定制中间件

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")  }

11、定制校验器

package main    import (  	"net/http"  	"reflect"  	"time"    	"github.com/gin-gonic/gin"  	"github.com/gin-gonic/gin/binding"  	"gopkg.in/go-playground/validator.v8"  )    // Booking contains binded and validated data.  type Booking struct {  	CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`  	CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`  }    func bookableDate(  	v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,  	field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,  ) bool {  	if date, ok := field.Interface().(time.Time); ok {  		today := time.Now()  		if today.Year() > date.Year() || today.YearDay() > date.YearDay() {  			return false  		}  	}  	return true  }    func main() {  	route := gin.Default()    	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {  		v.RegisterValidation("bookabledate", bookableDate)  	}    	route.GET("/bookable", getBookable)  	route.Run(":8085")  }    func getBookable(c *gin.Context) {  	var b Booking  	if err := c.ShouldBindWith(&b, binding.Query); err == nil {  		c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})  	} else {  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  	}  }
curl "localhost:8085/bookable?check_in=2018-04-16&check_out=2018-04-17"  {"message":"Booking dates are valid!"}    $ curl "localhost:8085/bookable?check_in=2018-03-08&check_out=2018-03-09"  {"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}

结构体的校验例子

12、定义路由的日志格式

默认的日志路由:

[GIN-debug] POST   /foo                      --> main.main.func1 (3 handlers)  [GIN-debug] GET    /bar                      --> main.main.func2 (3 handlers)  [GIN-debug] GET    /status                   --> main.main.func3 (3 handlers)
import (  	"log"  	"net/http"    	"github.com/gin-gonic/gin"  )    func main() {  	r := gin.Default()  	gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {  		log.Printf("endpoint %v %v %v %vn", httpMethod, absolutePath, handlerName, nuHandlers)  	}    	r.POST("/foo", func(c *gin.Context) {  		c.JSON(http.StatusOK, "foo")  	})    	r.GET("/bar", func(c *gin.Context) {  		c.JSON(http.StatusOK, "bar")  	})    	r.GET("/status", func(c *gin.Context) {  		c.JSON(http.StatusOK, "ok")  	})    	// Listen and Server in http://0.0.0.0:8080  	r.Run()  }

13、中间件中的goroutines的使用,在创建个新的时候,需要使用的是只读的拷贝引用

func main() {  	r := gin.Default()    	r.GET("/long_async", func(c *gin.Context) {  		// create copy to be used inside the goroutine  		cCp := c.Copy()  		go func() {  			// simulate a long task with time.Sleep(). 5 seconds  			time.Sleep(5 * time.Second)    			// note that you are using the copied context "cCp", IMPORTANT  			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  		log.Println("Done! in path " + c.Request.URL.Path)  	})    	// Listen and serve on 0.0.0.0:8080  	r.Run(":8080")  }  

14、如何优雅的停止服务

router := gin.Default()  router.GET("/", handler)  // [...]  endless.ListenAndServe(":4242", router)

15、分组路由的使用

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")  }

16、怎么写日志文件

func main() {      // Disable Console Color, you don't need console color when writing the logs to file.      gin.DisableConsoleColor()        // Logging to a file.      f, _ := os.Create("gin.log")      gin.DefaultWriter = io.MultiWriter(f)        // Use the following code if you need to write the logs to file and console at the same time.      // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)        router := gin.Default()      router.GET("/ping", func(c *gin.Context) {          c.String(200, "pong")      })        router.Run(":8080")  }

17、HTML渲染

使用LoadHTMLGlob() or LoadHTMLFiles()

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")  }

templates/index.tmpl

<html>  	<h1>  		{{ .title }}  	</h1>  </html>

在不同目录下的相同名称使用模板

func main() {  	router := gin.Default()  	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",  		})  	})  	router.Run(":8080")  }

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"    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") 

定制模板的功能函数-举个例子

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("%d/%02d/%02d", 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(":8080")  }

raw.tmpl

Date: {[{.now | formatAsDate}]}

输出结果

Date: 2017/07/01  

18、HTTP2服务推送-详细博客

前提条件:go版本1.8+

package main    import (  	"html/template"  	"log"    	"github.com/gin-gonic/gin"  )    var html = template.Must(template.New("https").Parse(`  <html>  <head>    <title>Https Test</title>    <script src="/assets/app.js"></script>  </head>  <body>    <h1 style="color:red;">Welcome, Ginner!</h1>  </body>  </html>  `))    func main() {  	r := gin.Default()  	r.Static("/assets", "./assets")  	r.SetHTMLTemplate(html)    	r.GET("/", func(c *gin.Context) {  		if pusher := c.Writer.Pusher(); pusher != nil {  			// use pusher.Push() to do server push  			if err := pusher.Push("/assets/app.js", nil); err != nil {  				log.Printf("Failed to push: %v", err)  			}  		}  		c.HTML(200, "https", gin.H{  			"status": "success",  		})  	})    	// Listen and Server in https://127.0.0.1:8080  	r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")  }

19、跨域JSONP

func main() {  	r := gin.Default()    	r.GET("/JSONP?callback=x", func(c *gin.Context) {  		data := map[string]interface{}{  			"foo": "bar",  		}    		//callback is x  		// Will output  :   x({"foo":"bar"})  		c.JSONP(http.StatusOK, data)  	})    	// Listen and serve on 0.0.0.0:8080  	r.Run(":8080")  }

20、map作为查询get参数或者post表单参数

POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1  Content-Type: application/x-www-form-urlencoded    names[first]=thinkerou&names[second]=tianou
func main() {  	router := gin.Default()    	router.POST("/post", func(c *gin.Context) {    		ids := c.QueryMap("ids")  		names := c.PostFormMap("names")    		fmt.Printf("ids: %v; names: %v", ids, names)  	})  	router.Run(":8080")  }
ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]

21、模型的绑定和校验

支持绑定的类型JSON, XML, YAML或者get标准参数

两种方式必须绑定和应该绑定

Bind,BindJSON,BindXML,BindQuery,BindYAML

ShouldBind,ShouldBindJSON,ShouldBindXML,ShouldBindQuery,ShouldBindYAML

如果非常确定是否绑定可以使用MustBindWithorShouldBindWith

属性可以binding:"required"

// Binding from JSON  type Login struct {  	User     string `form:"user" json:"user" xml:"user"  binding:"required"`  	Password string `form:"password" json:"password" xml:"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 err := c.ShouldBindJSON(&json); err != nil {  			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  			return  		}    		if json.User != "manu" || json.Password != "123" {  			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})  			return  		}    		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})  	})    	// Example for binding XML (  	//	<?xml version="1.0" encoding="UTF-8"?>  	//	<root>  	//		<user>user</user>  	//		<password>123</password>  	//	</root>)  	router.POST("/loginXML", func(c *gin.Context) {  		var xml Login  		if err := c.ShouldBindXML(&xml); err != nil {  			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  			return  		}    		if xml.User != "manu" || xml.Password != "123" {  			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})  			return  		}    		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})  	})    	// 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 err := c.ShouldBind(&form); err != nil {  			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  			return  		}    		if form.User != "manu" || form.Password != "123" {  			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})  			return  		}    		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})  	})    	// Listen and serve on 0.0.0.0:8080  	router.Run(":8080")  }

请求的示例

$ curl -v -X POST     http://localhost:8080/loginJSON     -H 'content-type: application/json'     -d '{ "user": "manu" }'  > POST /loginJSON HTTP/1.1  > Host: localhost:8080  > User-Agent: curl/7.51.0  > Accept: */*  > content-type: application/json  > Content-Length: 18  >  * upload completely sent off: 18 out of 18 bytes  < HTTP/1.1 400 Bad Request  < Content-Type: application/json; charset=utf-8  < Date: Fri, 04 Aug 2017 03:51:31 GMT  < Content-Length: 100  <  {"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}

如果使用binding:"-"forPassword则不会返回错误,跳过强制校验

22、URL编码绑定

package main    import (  	"github.com/gin-gonic/gin"  )    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.ShouldBindWith(&form, binding.Form)  		// or you can simply use autobinding with ShouldBind method:  		var form LoginForm  		// in this case proper binding will be automatically selected  		if c.ShouldBind(&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")  }
$ curl -v --form user=user --form password=password http://localhost:8080/login  

23、URL编码表单

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")  }

24、多模板渲染的使用-参考链接25、只绑定get查询

package main    import (  	"log"    	"github.com/gin-gonic/gin"  )    type Person struct {  	Name    string `form:"name"`  	Address string `form:"address"`  }    func main() {  	route := gin.Default()  	route.Any("/testing", startPage)  	route.Run(":8085")  }    func startPage(c *gin.Context) {  	var person Person  	if c.ShouldBindQuery(&person) == nil {  		log.Println("====== Only Bind By Query String ======")  		log.Println(person.Name)  		log.Println(person.Address)  	}  	c.String(200, "Success")  }

26、restful风格路径参数

func main() {  	router := gin.Default()    	// This handler will match /user/john but will not match /user/ or /user  	router.GET("/user/:name", func(c *gin.Context) {  		name := c.Param("name")  		c.String(http.StatusOK, "Hello %s", name)  	})    	// However, this one will match /user/john/ and also /user/john/send  	// If no other routers match /user/john, it will redirect to /user/john/  	router.GET("/user/:name/*action", func(c *gin.Context) {  		name := c.Param("name")  		action := c.Param("action")  		message := name + " is " + action  		c.String(http.StatusOK, message)  	})    	router.Run(":8080")  }

27、JSON编码HTML标签

强制前提版本go 版本1.6+

func main() {  	r := gin.Default()    	// Serves unicode entities  	r.GET("/json", func(c *gin.Context) {  		c.JSON(200, gin.H{  			"html": "<b>Hello, world!</b>",  		})  	})    	// Serves literal characters  	r.GET("/purejson", func(c *gin.Context) {  		c.PureJSON(200, gin.H{  			"html": "<b>Hello, world!</b>",  		})  	})    	// listen and serve on 0.0.0.0:8080  	r.Run(":8080")  }

28、查询和post表单

POST /post?id=1234&page=1 HTTP/1.1  Content-Type: application/x-www-form-urlencoded    name=manu&message=this_is_great
func main() {  	router := gin.Default()    	router.POST("/post", func(c *gin.Context) {    		id := c.Query("id")  		page := c.DefaultQuery("page", "0")  		name := c.PostForm("name")  		message := c.PostForm("message")    		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)  	})  	router.Run(":8080")  }

输出内容

id: 1234; page: 1; name: manu; message: this_is_great  

29、string参数

func main() {  	router := gin.Default()    	// Query string parameters are parsed using the existing underlying request object.  	// The request responds to a 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")    		c.String(http.StatusOK, "Hello %s %s", firstname, lastname)  	})  	router.Run(":8080")  }

30、重定向

r.GET("/test", func(c *gin.Context) {  	c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")  })  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"})  })

31、运行多个服务

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 {  		return server01.ListenAndServe()  	})    	g.Go(func() error {  		return server02.ListenAndServe()  	})    	if err := g.Wait(); err != nil {  		log.Fatal(err)  	}  }  

32、安全的JSON

func main() {  	r := gin.Default()    	// You can also use your own secure json prefix  	// r.SecureJsonPrefix(")]}',n")    	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")  }

33、从阅读器提供数据

func main() {  	router := gin.Default()  	router.GET("/someDataFromReader", func(c *gin.Context) {  		response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")  		if err != nil || response.StatusCode != http.StatusOK {  			c.Status(http.StatusServiceUnavailable)  			return  		}    		reader := response.Body  		contentLength := response.ContentLength  		contentType := response.Header.Get("Content-Type")    		extraHeaders := map[string]string{  			"Content-Disposition": `attachment; filename="gopher.png"`,  		}    		c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)  	})  	router.Run(":8080")  }

34、服务静态文件

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")  }

35、set和get一个cookie

import (      "fmt"        "github.com/gin-gonic/gin"  )    func main() {        router := gin.Default()        router.GET("/cookie", func(c *gin.Context) {            cookie, err := c.Cookie("gin_cookie")            if err != nil {              cookie = "NotSet"              c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)          }            fmt.Printf("Cookie value: %s n", cookie)      })        router.Run()  }

36、支持加密

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

37、试着绑定body给不同的结构体

type formA struct {    Foo string `json:"foo" xml:"foo" binding:"required"`  }    type formB struct {    Bar string `json:"bar" xml:"bar" binding:"required"`  }    func SomeHandler(c *gin.Context) {    objA := formA{}    objB := formB{}    // This c.ShouldBind consumes c.Request.Body and it cannot be reused.    if errA := c.ShouldBind(&objA); errA == nil {      c.String(http.StatusOK, `the body should be formA`)    // Always an error is occurred by this because c.Request.Body is EOF now.    } else if errB := c.ShouldBind(&objB); errB == nil {      c.String(http.StatusOK, `the body should be formB`)    } else {      ...    }  }
func SomeHandler(c *gin.Context) {    objA := formA{}    objB := formB{}    // This reads c.Request.Body and stores the result into the context.    if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {      c.String(http.StatusOK, `the body should be formA`)    // At this time, it reuses body stored in the context.    } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {      c.String(http.StatusOK, `the body should be formB JSON`)    // And it can accepts other formats    } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {      c.String(http.StatusOK, `the body should be formB XML`)    } else {      ...    }  }

38、多个文件

func main() {  	router := gin.Default()  	// Set a lower memory limit for multipart forms (default is 32 MiB)  	// router.MaxMultipartMemory = 8 << 20  // 8 MiB  	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")  }

调用

curl -X POST http://localhost:8080/upload     -F "upload[]=@/Users/appleboy/test1.zip"     -F "upload[]=@/Users/appleboy/test2.zip"     -H "Content-Type: multipart/form-data"

39、单个文件

func main() {  	router := gin.Default()  	// Set a lower memory limit for multipart forms (default is 32 MiB)  	// router.MaxMultipartMemory = 8 << 20  // 8 MiB  	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")  }

调用

curl -X POST http://localhost:8080/upload     -F "file=@/Users/appleboy/test.zip"     -H "Content-Type: multipart/form-data"

40、使用基本鉴权的中间件

// simulate some private data  var secrets = gin.H{  	"foo":    gin.H{"email": "[email protected]", "phone": "123433"},  	"austin": gin.H{"email": "[email protected]", "phone": "666"},  	"lena":   gin.H{"email": "[email protected]", "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")  }

41、使用HTTP方法

func main() {  	// Creates a gin router with default middleware:  	// logger and recovery (crash-free) middleware  	router := gin.Default()    	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)    	// By default it serves on :8080 unless a  	// PORT environment variable was defined.  	router.Run()  	// router.Run(":3000") for a hard coded port  }

42、使用中间件

func main() {  	// Creates a router without any middleware by default  	r := gin.New()    	// Global middleware  	// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.  	// By default gin.DefaultWriter = os.Stdout  	r.Use(gin.Logger())    	// Recovery middleware recovers from any panics and writes a 500 if there was one.  	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")  }

43、默认没有中间件

使用r := gin.New()代替  // Default With the Logger and Recovery middleware already attached  r := gin.Default()

44、XML/JSON/YAML/ProtoBuf 渲染

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})  	})    	r.GET("/someProtoBuf", func(c *gin.Context) {  		reps := []int64{int64(1), int64(2)}  		label := "test"  		// The specific definition of protobuf is written in the testdata/protoexample file.  		data := &protoexample.Test{  			Label: &label,  			Reps:  reps,  		}  		// Note that data becomes binary data in the response  		// Will output protoexample.Test protobuf serialized data  		c.ProtoBuf(http.StatusOK, data)  	})    	// Listen and serve on 0.0.0.0:8080  	r.Run(":8080")  }