feat(Go-StudyExample): 2020/12/9 :新增Gin框架使用示例
This commit is contained in:
@@ -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": "<br>",
|
||||
}
|
||||
|
||||
// 输出 : {"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())
|
||||
}
|
||||
}
|
||||
@@ -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})
|
||||
})
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @Author : huangzj
|
||||
* @Time : 2020/12/8 9:27
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
package gin
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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 (
|
||||
// <?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"})
|
||||
})
|
||||
}
|
||||
|
||||
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"})
|
||||
})
|
||||
|
||||
}
|
||||
@@ -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
|
||||
`
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @Author : huangzj
|
||||
* @Time : 2020/12/8 9:31
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
package gin
|
||||
@@ -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()})
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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"})
|
||||
})
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @Author : huangzj
|
||||
* @Time : 2020/12/8 9:39
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
package gin
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user