diff --git a/example/gin/1-AsciiJson_test.go b/example/gin/1-AsciiJson_test.go
new file mode 100644
index 0000000..a9e180d
--- /dev/null
+++ b/example/gin/1-AsciiJson_test.go
@@ -0,0 +1,34 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 9:18
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+)
+import "github.com/gin-gonic/gin"
+
+func TestAsciiJson(t *testing.T) {
+ r := gin.Default()
+
+ r.GET("/someJSON", func(c *gin.Context) {
+ data := map[string]interface{}{
+ "lang": "GO语言",
+ "tag": "
",
+ }
+
+ // 输出 : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
+ c.AsciiJSON(http.StatusOK, data)
+ })
+
+ // 监听并在 0.0.0.0:8080 上启动服务
+ err := r.Run(":8080")
+ if err != nil {
+ fmt.Println(err.Error())
+ }
+}
diff --git a/example/gin/10-Render_test.go b/example/gin/10-Render_test.go
new file mode 100644
index 0000000..e0f6507
--- /dev/null
+++ b/example/gin/10-Render_test.go
@@ -0,0 +1,86 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 9:46
+ * @Description:
+ */
+
+package gin
+
+import (
+ "github.com/gin-gonic/gin"
+ "net/http"
+ "testing"
+)
+
+type Test struct {
+ Label *string
+ Reps []int64
+}
+
+func TestRender(t *testing.T) {
+ r := gin.Default()
+
+ byMap(r) //直接通过map返回
+
+ byStructWithAlias(r) //通过结构体返回,并设置结构体别名
+
+ byXml(r) //返回xml报文
+
+ byYaml(r) //返回YAML报文
+
+ byProtoBuf(r) //返回序列化的二进制数据
+
+ // 监听并在 0.0.0.0:8080 上启动服务
+ r.Run(":8080")
+}
+
+func byProtoBuf(r *gin.Engine) {
+ r.GET("/someProtoBuf", func(c *gin.Context) {
+ reps := []int64{int64(1), int64(2)}
+ label := "test"
+ // protobuf 的具体定义写在 testdata/protoexample 文件中。
+ data := &Test{
+ Label: &label,
+ Reps: reps,
+ }
+ // 请注意,数据在响应中变为二进制数据
+ // 将输出被 protoexample.Test protobuf 序列化了的数据
+ c.ProtoBuf(http.StatusOK, data)
+ })
+}
+
+func byYaml(r *gin.Engine) {
+ r.GET("/someYAML", func(c *gin.Context) {
+ c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
+ })
+}
+
+func byXml(r *gin.Engine) {
+ r.GET("/someXML", func(c *gin.Context) {
+ c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
+ })
+}
+
+func byStructWithAlias(r *gin.Engine) {
+ r.GET("/moreJSON", func(c *gin.Context) {
+ // 你也可以使用一个结构体
+ var msg struct {
+ Name string `json:"user"`
+ Message string
+ Number int
+ }
+ msg.Name = "Lena"
+ msg.Message = "hey"
+ msg.Number = 123
+ // 注意 msg.Name 在 JSON 中变成了 "user"
+ // 将输出:{"user": "Lena", "Message": "hey", "Number": 123}
+ c.JSON(http.StatusOK, msg)
+ })
+}
+
+func byMap(r *gin.Engine) {
+ // gin.H 是 map[string]interface{} 的一种快捷方式
+ r.GET("/someJSON", func(c *gin.Context) {
+ c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
+ })
+}
diff --git a/example/gin/11-OneFile_test.go b/example/gin/11-OneFile_test.go
new file mode 100644
index 0000000..8fe0807
--- /dev/null
+++ b/example/gin/11-OneFile_test.go
@@ -0,0 +1,40 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 10:51
+ * @Description:单文件上传
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "log"
+ "net/http"
+ "testing"
+)
+
+func TestOneFile(t *testing.T) {
+ router := gin.Default()
+ // 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
+ router.MaxMultipartMemory = 8 << 20 // 8 MiB
+ router.POST("/upload", func(c *gin.Context) {
+ // 单文件
+ fileHeader, _ := c.FormFile("file")
+ log.Println(fileHeader.Filename)
+
+ var content []byte
+ file, _ := fileHeader.Open()
+ defer file.Close()
+ _, _ = file.Read(content)
+ fmt.Println(string(content)) //输出文件内容
+
+ //上传文件至指定目录
+ err := c.SaveUploadedFile(fileHeader, "C://Users//admin//Desktop")
+ if err != nil {
+ panic(err)
+ }
+ c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", fileHeader.Filename))
+ })
+ router.Run(":8080")
+}
diff --git a/example/gin/12-MultiFile_test.go b/example/gin/12-MultiFile_test.go
new file mode 100644
index 0000000..59e7b2f
--- /dev/null
+++ b/example/gin/12-MultiFile_test.go
@@ -0,0 +1,42 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 11:10
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "log"
+ "net/http"
+ "testing"
+)
+
+//上传命令,指定多文件数组
+// 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"
+func TestMultiFile(t *testing.T) {
+ router := gin.Default()
+ // 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
+ // router.MaxMultipartMemory = 8 << 20 // 8 MiB
+ router.POST("/upload", func(c *gin.Context) {
+ // Multipart form
+ form, _ := c.MultipartForm()
+ fileHeaders := form.File["upload[]"]
+
+ for _, header := range fileHeaders {
+ log.Println(header.Filename)
+ file, _ := header.Open()
+ var content []byte
+ file.Read(content) //读取文件
+
+ // c.SaveUploadedFile(file, dst) //上传文件至指定目录
+ }
+ c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(fileHeaders)))
+ })
+ router.Run(":8080")
+}
diff --git a/example/gin/15-GeneralSwitch_test.go b/example/gin/15-GeneralSwitch_test.go
new file mode 100644
index 0000000..303735c
--- /dev/null
+++ b/example/gin/15-GeneralSwitch_test.go
@@ -0,0 +1,52 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 11:18
+ * @Description:
+ */
+
+package gin
+
+import (
+ "context"
+ "github.com/gin-gonic/gin"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "testing"
+ "time"
+)
+
+func TestGeneralSwitch(t *testing.T) {
+ 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() {
+ // 服务连接
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Fatalf("listen: %s\n", err)
+ }
+ }()
+
+ // 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
+ 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 exiting")
+
+}
diff --git a/example/gin/2-HtmlRender_test.go b/example/gin/2-HtmlRender_test.go
new file mode 100644
index 0000000..444cbe5
--- /dev/null
+++ b/example/gin/2-HtmlRender_test.go
@@ -0,0 +1,7 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 9:27
+ * @Description:
+ */
+
+package gin
diff --git a/example/gin/20-ShouldBind_test.go b/example/gin/20-ShouldBind_test.go
new file mode 100644
index 0000000..8daa24f
--- /dev/null
+++ b/example/gin/20-ShouldBind_test.go
@@ -0,0 +1,60 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 11:33
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "github.com/gin-gonic/gin/binding"
+ "testing"
+)
+
+type formA struct {
+ Foo string `json:"foo" xml:"foo" binding:"required"`
+}
+
+type formB struct {
+ Bar string `json:"bar" xml:"bar" binding:"required"`
+}
+
+func TestBind(t *testing.T) {
+ router := gin.Default()
+
+ bindOnce(router) //只能绑定一次
+ bindMore(router) //可以多次绑定
+}
+
+func bindMore(router *gin.Engine) {
+ objA := formA{}
+ objB := formB{}
+
+ // 读取 c.Request.Body 并将结果存入上下文,所以下一次的读取可以从上下文中拿到,达到重复读取的目的
+ // 会对性能造成轻微影响,如果调用一次就能完成绑定的话,那就不要用这个方法。
+ router.GET("bindMany", func(context *gin.Context) {
+ if errA := context.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
+ fmt.Println(objA.Foo)
+ //绑定不同的格式,这边演示xml的格式进行绑定
+ } else if errB := context.ShouldBindBodyWith(&objB, binding.XML); errB == nil {
+ fmt.Println(objB.Bar)
+ }
+ })
+}
+
+func bindOnce(router *gin.Engine) {
+ objA := formA{}
+ objB := formB{}
+
+ //c.ShouldBind 使用了 c.Request.Body,不可重用。请求是数据流,没有存储下来则只能使用一次
+ router.GET("bind", func(context *gin.Context) {
+ //绑定Body数据到结构体.
+ if errA := context.ShouldBind(&objA); errA == nil {
+ fmt.Println(objA.Foo)
+ } else if errB := context.ShouldBind(&objB); errB == nil {
+ fmt.Println(objB.Bar)
+ }
+ })
+}
diff --git a/example/gin/22-BindValid_test.go b/example/gin/22-BindValid_test.go
new file mode 100644
index 0000000..ead101b
--- /dev/null
+++ b/example/gin/22-BindValid_test.go
@@ -0,0 +1,133 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 11:58
+ * @Description:模型绑定和验证
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "net/http"
+ "testing"
+)
+
+func doc() string {
+ return `
+ 要将请求体绑定到结构体中,使用模型绑定。 Gin 目前支持 JSON、XML、YAML 和标准表单值的绑定(foo=bar&boo=baz)。
+
+ Gin 使用 go-playground/validator.v8 进行验证。 查看标签用法的全部文档(https://github.com/go-playground/validator)
+
+ 使用时,需要在要绑定的所有字段上,设置相应的 tag。 例如,使用 JSON 绑定时,设置字段标签为 json:"fieldname"。
+ Gin 提供了两类绑定方法:
+ Type - Must bind
+ Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
+ Behavior - 这些方法属于 MustBindWith 的具体调用。 如果发生绑定错误,则请求终止,并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)。响应状态码被设置为 400 并且 Content-Type 被设置为 text/plain; charset=utf-8。 如果您在此之后尝试设置响应状态码,Gin 会输出日志 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422。 如果您希望更好地控制绑定,考虑使用 ShouldBind 等效方法。
+
+ Type - Should bind
+ Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
+ Behavior - 这些方法属于 ShouldBindWith 的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。
+
+
+ 使用 Bind 方法时,Gin 会尝试根据 Content-Type 推断如何绑定。 如果你明确知道要绑定什么,可以使用 MustBindWith 或 ShouldBindWith。
+ 你也可以指定必须绑定的字段。 如果一个字段的 tag 加上了 binding:"required",但绑定时是空值,Gin 会报错。
+`
+}
+
+type Login struct {
+ User string `form:"user" json:"user" xml:"user" binding:"required"`
+ Password string `form:"password" json:"password" xml:"password" binding:"required"`
+}
+
+func TestBindValid(t *testing.T) {
+ router := gin.Default()
+ BindJson(router) //对结构体中的json设置字段名进行绑定,如果没有对应字段则会报错
+
+ BindXml(router) //对结构体中的Xml设置字段名进行绑定,如果没有对应字段则会报错
+
+ BindHtml(router) //对html输入的地址栏参数进行绑定
+
+ //请求示例
+ _ = `$ 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"}
+ `
+ fmt.Println("上述为请求示例,因为在结构体的tag上设置了必须校验,所以当一个字段没有进行输入的时候,就会报错,除非是去掉对应的tag设置或者是传输正确的字段")
+}
+
+func BindHtml(router *gin.Engine) {
+ // 绑定 HTML 表单 (user=manu&password=123)
+ router.POST("/loginForm", func(c *gin.Context) {
+ var form Login
+ // 根据 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"})
+ })
+}
+
+func BindXml(router *gin.Engine) {
+ // 绑定 XML (
+ //
+ //
+ // user
+ // 123
+ // )
+ 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"})
+ })
+}
+
+func BindJson(router *gin.Engine) {
+ // 绑定 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"})
+ })
+
+}
diff --git a/example/gin/24-BindUri_test.go b/example/gin/24-BindUri_test.go
new file mode 100644
index 0000000..8026acf
--- /dev/null
+++ b/example/gin/24-BindUri_test.go
@@ -0,0 +1,38 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 14:06
+ * @Description:直接绑定Uri参数
+ */
+
+package gin
+
+import (
+ "github.com/gin-gonic/gin"
+ "testing"
+)
+
+type Person struct {
+ ID string `uri:"id" binding:"required,uuid"`
+ Name string `uri:"name" binding:"required"`
+}
+
+func TestBindUri(t *testing.T) {
+ route := gin.Default()
+ //这边的【:name】和【:id】说明了对应的该Uri,传入的参数分别是叫做name和id.在下面的Bind直接进行映射
+ route.GET("/uri/: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")
+
+ //请求示例
+ _ = `
+ 这边可以看到uri后面的两个参数就是 name 和 id,直接不用声明名称.
+ $ curl -v localhost:8088/uri/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
+ $ curl -v localhost:8088/uri/thinkerou/not-uuid
+ `
+}
diff --git a/example/gin/28-DiyHttpConfig_test.go b/example/gin/28-DiyHttpConfig_test.go
new file mode 100644
index 0000000..f2eb258
--- /dev/null
+++ b/example/gin/28-DiyHttpConfig_test.go
@@ -0,0 +1,29 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 14:14
+ * @Description:自定义Http配置
+ */
+
+package gin
+
+import (
+ "github.com/gin-gonic/gin"
+ "net/http"
+ "testing"
+ "time"
+)
+
+func TestHttpConfig(t *testing.T) {
+ router := gin.Default()
+
+ //最重要的是可以通过自定义Handler来处理不同类型的请求,这边的请求是说监听的不同端口.
+ //而不同的请求方法,可以处理对应的不同路径的请求,可以把一个类型的路径用一个相应的方法进行处理.
+ s := &http.Server{
+ Addr: ":8080", //监听地址
+ Handler: router, //处理类可自定义
+ ReadTimeout: 10 * time.Second, //读超时
+ WriteTimeout: 10 * time.Second, //写超时
+ MaxHeaderBytes: 1 << 20,
+ }
+ s.ListenAndServe()
+}
diff --git a/example/gin/29-DiyMiddleWare_test.go b/example/gin/29-DiyMiddleWare_test.go
new file mode 100644
index 0000000..2f3438f
--- /dev/null
+++ b/example/gin/29-DiyMiddleWare_test.go
@@ -0,0 +1,51 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 14:45
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "log"
+ "testing"
+ "time"
+)
+
+func Logger() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ t := time.Now()
+
+ // 请求前
+ fmt.Println(fmt.Sprintf("记录日志,本次请求的参数为:%v", c.Params))
+
+ c.Next()
+
+ // 请求后
+ latency := time.Since(t)
+ log.Print(latency)
+
+ // 获取发送的 status
+ status := c.Writer.Status()
+ log.Println(status)
+ }
+}
+
+//在请求前后者请求后自定义中间件处理
+func TestMiddleWare(t *testing.T) {
+ router := gin.Default() //Default会返回两个中间件 Logger(), Recovery().这接口调用之前和之后进行处理,所以我们也可以自定义中间件
+
+ router.Use(Logger())
+
+ router.GET("/test", func(c *gin.Context) {
+ example := c.MustGet("example").(string)
+
+ // 打印:"12345"
+ log.Println(example)
+ })
+
+ // 监听并在 0.0.0.0:8080 上启动服务
+ router.Run(":8080")
+}
diff --git a/example/gin/3-Http2Server_test.go b/example/gin/3-Http2Server_test.go
new file mode 100644
index 0000000..ddeba6a
--- /dev/null
+++ b/example/gin/3-Http2Server_test.go
@@ -0,0 +1,7 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 9:31
+ * @Description:
+ */
+
+package gin
diff --git a/example/gin/30-DiyStructValid_test.go b/example/gin/30-DiyStructValid_test.go
new file mode 100644
index 0000000..9c15e36
--- /dev/null
+++ b/example/gin/30-DiyStructValid_test.go
@@ -0,0 +1,64 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 15:12
+ * @Description:
+ */
+
+package gin
+
+import (
+ "github.com/gin-gonic/gin"
+ "github.com/gin-gonic/gin/binding"
+ "github.com/go-playground/validator/v10"
+ "net/http"
+ "testing"
+ "time"
+)
+
+// Booking 包含绑定和验证的数据。
+type Booking struct {
+ CheckIn time.Time `form:"check_in" binding:"required,diyValid" time_format:"2006-01-02"`
+ CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
+}
+
+//read note 自定义的校验类,需要实现 validator.Func方法(参数是validator.FieldLevel,这里把所有的参数都集合进去了,之前的版本是多个参数)
+func bookDataValid(level validator.FieldLevel) bool {
+ if date, ok := level.Field().Interface().(time.Time); ok {
+ today := time.Now()
+ if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
+ return false
+ }
+ }
+
+ return true
+}
+
+func TestDiyStructValid(t *testing.T) {
+ route := gin.Default()
+
+ //read note 注册自定义的校验类,这边设置的key就是需要在结构体上绑定数据时候书写的tag标签,注意看上面Booking 的CheckIn标签(只有在结构体上书写了该标签,这个校验器才会去校验对应的字段)
+ if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
+ _ = v.RegisterValidation("diyValid", bookDataValid)
+ }
+
+ route.GET("/bookable", getBookable)
+ _ = route.Run(":8085")
+
+ //请求示例
+ _ = `
+ $ 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 'diyValid' tag"}
+ `
+}
+
+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()})
+ }
+}
diff --git a/example/gin/31-ReadCookie_test.go b/example/gin/31-ReadCookie_test.go
new file mode 100644
index 0000000..44a679d
--- /dev/null
+++ b/example/gin/31-ReadCookie_test.go
@@ -0,0 +1,32 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 15:27
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "testing"
+)
+
+func TestReadCookie(t *testing.T) {
+
+ router := gin.Default()
+
+ router.GET("/cookie", func(c *gin.Context) {
+
+ //read note 这边需要web前端发送对应的cookie
+ cookie, err := c.Cookie("gin_cookie")
+
+ //read note 设置对应值的cookie
+ if err != nil {
+ cookie = "NotSet"
+ c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
+ }
+ fmt.Printf("Cookie value: %s \n", cookie)
+ })
+ _ = router.Run()
+}
diff --git a/example/gin/32-RouterMatch_test.go b/example/gin/32-RouterMatch_test.go
new file mode 100644
index 0000000..b637280
--- /dev/null
+++ b/example/gin/32-RouterMatch_test.go
@@ -0,0 +1,42 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 15:54
+ * @Description:
+ */
+
+package gin
+
+import (
+ "github.com/gin-gonic/gin"
+ "net/http"
+ "testing"
+)
+
+func TestRouterMatch(t *testing.T) {
+ router := gin.Default()
+
+ OnlyMatch(router) //只能匹配到指定格式
+
+ MathMany(router) //*匹配多重字段
+
+ _ = router.Run(":8080")
+}
+
+func MathMany(router *gin.Engine) {
+ // 此 handler 将匹配 /user/john/ 和 /user/john/send
+ // 如果没有其他路由匹配 /user/john,它将重定向到 /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)
+ })
+}
+
+func OnlyMatch(router *gin.Engine) {
+ // 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /user
+ router.GET("/user/:name", func(c *gin.Context) {
+ name := c.Param("name")
+ c.String(http.StatusOK, "Hello %s", name)
+ })
+}
diff --git a/example/gin/33-RouteGroup_test.go b/example/gin/33-RouteGroup_test.go
new file mode 100644
index 0000000..80f37b5
--- /dev/null
+++ b/example/gin/33-RouteGroup_test.go
@@ -0,0 +1,48 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 16:15
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "testing"
+)
+
+//路有组就是把相同前缀的路由路径分在一起。
+func TestRouteGroup(t *testing.T) {
+ router := gin.Default()
+
+ // 简单的路由组: v1
+ v1 := router.Group("/v1")
+ {
+ v1.POST("/login", func(context *gin.Context) {
+ fmt.Println(context.Params)
+ })
+ v1.POST("/submit", func(context *gin.Context) {
+ fmt.Println(context.Params)
+ })
+ v1.POST("/read", func(context *gin.Context) {
+ fmt.Println(context.Params)
+ })
+ }
+
+ // 简单的路由组: v2
+ v2 := router.Group("/v2")
+ {
+ v2.POST("/login", func(context *gin.Context) {
+ fmt.Println(context.Params)
+ })
+ v2.POST("/submit", func(context *gin.Context) {
+ fmt.Println(context.Params)
+ })
+ v2.POST("/read", func(context *gin.Context) {
+ fmt.Println(context.Params)
+ })
+ }
+
+ _ = router.Run(":8080")
+}
diff --git a/example/gin/35-Redirect_test.go b/example/gin/35-Redirect_test.go
new file mode 100644
index 0000000..be5b5a7
--- /dev/null
+++ b/example/gin/35-Redirect_test.go
@@ -0,0 +1,25 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 16:22
+ * @Description:
+ */
+
+package gin
+
+import (
+ "github.com/gin-gonic/gin"
+ "testing"
+)
+
+//重定向
+func TestRedirect(t *testing.T) {
+ r := gin.Default()
+
+ 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"})
+ })
+}
diff --git a/example/gin/4-Jsonp_test.go b/example/gin/4-Jsonp_test.go
new file mode 100644
index 0000000..83d30bd
--- /dev/null
+++ b/example/gin/4-Jsonp_test.go
@@ -0,0 +1,34 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 9:31
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "net/http"
+ "testing"
+)
+
+func TestJsonp(t *testing.T) {
+ r := gin.Default()
+
+ r.GET("/JSONP?callback=x", func(c *gin.Context) {
+ data := map[string]interface{}{
+ "foo": "bar",
+ }
+
+ // callback 是 x
+ // 将输出:x({\"foo\":\"bar\"})
+ c.JSONP(http.StatusOK, data)
+ })
+
+ // 监听并在 0.0.0.0:8080 上启动服务
+ err := r.Run(":8080")
+ if err != nil {
+ fmt.Println(err.Error())
+ }
+}
diff --git a/example/gin/7-PureJson_test.go b/example/gin/7-PureJson_test.go
new file mode 100644
index 0000000..5c55057
--- /dev/null
+++ b/example/gin/7-PureJson_test.go
@@ -0,0 +1,7 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 9:39
+ * @Description:
+ */
+
+package gin
diff --git a/example/gin/8-QueryPostForm_test.go b/example/gin/8-QueryPostForm_test.go
new file mode 100644
index 0000000..a5de0e3
--- /dev/null
+++ b/example/gin/8-QueryPostForm_test.go
@@ -0,0 +1,34 @@
+/*
+ * @Author : huangzj
+ * @Time : 2020/12/8 9:39
+ * @Description:
+ */
+
+package gin
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "testing"
+)
+
+//read note POST /post?id=1234&page=1 HTTP/1.1
+// Content-Type: application/x-www-form-urlencoded
+// name=manu&message=this_is_great
+
+//read note Query是用来解析地址栏中的参数
+// PostForm是用来解析报文体中的参数
+func TestQueryPostForm(t *testing.T) {
+ 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")
+}
diff --git a/example/gin/readme.md b/example/gin/readme.md
new file mode 100644
index 0000000..1fa24d5
--- /dev/null
+++ b/example/gin/readme.md
@@ -0,0 +1,15 @@
+Gin使用教程
+
+ https://learnku.com/docs/gin-gonic/2019
+
+获取Gin框架命令
+
+ go get -u github.com/gin-gonic/gin
+
+Context官方Blog
+
+ https://blog.golang.org/context
+
+深度解密Go Context
+
+ https://learnku.com/articles/29877
\ No newline at end of file
diff --git a/readme.md b/readme.md
index d054024..85a8d51 100644
--- a/readme.md
+++ b/readme.md
@@ -13,6 +13,7 @@ json与struct之间转换处理工具使用示例
2020/7/13 : 新增 网络及端口测试、传值和传址的比较、切片测试、Map测试、通道测试、测试init调用、测试error、defer关键字、recover测试、测试携程的异常捕获、矩阵旋转
+2020/12/9 :新增Gin框架使用示例
# mod vendor模式加载包
通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到