Gin 文档学习

1.自定义模板渲染器

// 自定义html模板渲染器,要指定所有的html路径,不推荐
    html := template.Must(template.ParseFiles(
        "templates/login.html",
        "templates/users/index.html",
        "templates/center/index.html",
        ))
//应用这些模板
    router.SetHTMLTemplate(html)
    router.GET("/users/index", func(context *gin.Context) {
        context.HTML(http.StatusOK, "users/index.html", gin.H{
            "title": "users/index.html",
        })
    })

2.自定义模板功能

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("./templates/raw.html")

	router.GET("/raw", func(c *gin.Context) {
		c.HTML(http.StatusOK, "raw.html", map[string]interface{}{
			"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
		})
	})

	router.Run(":9999")
}

raw.html

<body>
<!--等同于 formatAsDate .now-->
date:   <{.now | formatAsDate}>
</body>

在这里插入图片描述

3.Multipart/Urlencoded 绑定

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) {
		// 你可以使用显式绑定声明绑定 multipart form:
		//c.ShouldBindWith(&form, binding.Form)
		// 或者简单地使用 ShouldBind 方法自动绑定:
		var form LoginForm
		// 在这种情况下,将自动选择合适的绑定
		if c.ShouldBind(&form) == nil {
			if form.User == "Winnie-OCEAN" && form.Password == "789" {
				c.JSON(200, gin.H{"status": "you are logged in",
					"user":     form.User,
					"password": form.Password,
				})
			} else {
				c.JSON(401, gin.H{"status": "unauthorized"})
			}
		}
	})
	router.Run()
}

在这里插入图片描述
或者

router.POST("/login", func(c *gin.Context) {
		// 或者简单地使用 ShouldBind 方法自动绑定:
		//c.ShouldBind(&form)
		var form LoginForm
		// 你可以使用显式绑定声明绑定 multipart form:
		if c.ShouldBindWith(&form, binding.Form) == nil {
			if form.User == "OCEAN" && form.Password == "123456789" {
				c.JSON(200, gin.H{"status": "you are logged in",
					"user":     form.User,
					"password": form.Password,
				})
			} else {
				c.JSON(401, gin.H{"status": "unauthorized"})
			}
		}
	})

在这里插入图片描述

4.Multipart/Urlencoded 表单

package main

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

func main() {
	engine := gin.Default()
	engine.POST("/post_form", func(context *gin.Context) {
		//获取post过来的名为user的值
		user := context.PostForm("user")
		//获取post过来的名为password的值,第二个参数为默认值
		password := context.DefaultPostForm("password", "123456")
		context.JSON(http.StatusOK, gin.H{
			"success":  "login in",
			"username": user,
			"password": password,
		})
	})
	engine.Run()
}

在这里插入图片描述

5.PureJSON

JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003c。如果要按字面对这些字符进行编码,则可以使用 PureJSON。

package main

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

func main() {
	r := gin.Default()
	//结果为 {"html":"\u003cb\u003eHello, world!\u003c/b\u003e"}
	// 提供 unicode 实体
	r.GET("/json", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"html": "<b>Hello, world!</b>",
		})
	})
	//输出结果{"html":"<b>Hello, world!</b>"}
	// 提供字面字符
	r.GET("/purejson", func(c *gin.Context) {
		c.PureJSON(200, gin.H{
			"html": "<b>Hello, world!</b>",
		})
	})

	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":8080")
}

6.提取url参数同时获取post或来的数据 Query 和 post form

package main

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

func main() {
	engine := gin.Default()
	engine.POST("/post_query", func(context *gin.Context) {
		username := context.Query("username")
		password := context.DefaultQuery("password", "123")
		age := context.PostForm("age")
		page := context.PostForm("page")
		context.JSON(200, gin.H{
			"username": username,
			"password": password,
			"age":      age,
			"page":     page,
		})

	})
	engine.Run(":9999")
}

在这里插入图片描述

7.SecureJSON 防止 json 劫持

JSON劫持,其实就是恶意网站,通过<script>标签获取你的JSON数据,因为JSON数组默认为是可执行的JS,所以通过这种方式,可以获得你的敏感数据。最前面有个while(1);前缀,这就可以在<script>标签执行我们返回的数据时,就可以无限循环,阻止后面数组数据的执行,防止数据被劫持。

func main() {
	r := gin.Default()
	r.GET("/someJSON", func(c *gin.Context) {
		names := []string{"winnie", "ocean", "22"}
		// 将输出:while(1);["winnie", "ocean", "22"]
		c.SecureJSON(http.StatusOK, names)
	})
	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":8080")
}

Gin默认的防JSON劫持的前缀是while(1);我们可以改变,通过r.SecureJsonPrefix方法设置即可,如:

func main() {
	r := gin.Default()
	//你也可以使用自己的 SecureJSON 前缀
	r.SecureJsonPrefix(")]}',\n")
	r.GET("/someJSON", func(c *gin.Context) {
		names := []string{"winnie", "ocean", "22"}
		// 将输出:
		//)]}',
		//["winnie","ocean","22"]
		c.SecureJSON(http.StatusOK, names)
	})
	r.Run(":8080")
}

8. xml json yaml protobuf

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/testdata/protoexample"
	"net/http"
)

func main() {
	r := gin.Default()
	// gin.H 是 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) {
		// 使用一个结构体
		var msg struct {
			// 注意 msg.Name 在 JSON 中变成了 "user"
			Name    string `json:"user"`
			Message string
			Number  int
		}
		msg.Name = "Lena"
		msg.Message = "hey"
		msg.Number = 123
		// 将输出:{"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"
		// protobuf 的具体定义写在 testdata/protoexample 文件中。
		data := &protoexample.Test{
			Label: &label,
			Reps:  reps,
		}
		// 请注意,数据在响应中变为二进制数据
		// 将输出(产生) data被ProtoBuf序列化了 的数据,生成一个二进制文件
		
		// 序列化:将数据结构或者对象转化成二进制串的过程
		
		c.ProtoBuf(http.StatusOK, data)
	})
	r.Run()
}

9.上传文件

9.1 单文件

func main() {
	engine := gin.Default()
	// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
	// 限制处理该文件所占用的最大内存
	engine.MaxMultipartMemory = 8 << 20 // 8 MiB
	engine.POST("/post", func(context *gin.Context) {
		//单文件
		file, err := context.FormFile("picture")
		if err != nil {
			log.Fatal(err)
		}
		//打印文件名
		log.Println(file.Filename)
		//设置上传路径  没有就默认当前目录下
		dst := "./" + file.Filename
		// 上传文件至指定的完整文件路径
		context.SaveUploadedFile(file, dst)
		context.String(200, "upload ok!!")
	})
	engine.Run()
}

在这里插入图片描述

9.2 多文件

func main() {
	engine := gin.Default()
	engine.MaxMultipartMemory = 8 << 20 //8Mib
	engine.POST("/files", func(context *gin.Context) {
		//MultipartForm是经过解析的 多部分表单,包括文件上传。
		form, _ := context.MultipartForm() //多部份表单
		files := form.File["uploads[]"]    //收集名为 uploads[]的多个文件
		//循环遍历输出文件名
		for _, file := range files {
			log.Println(file.Filename)
			//上传文件 至 指定路径 没有路径,默认根目录。
			context.SaveUploadedFile(file, file.Filename)
		}
		context.String(200, "upload files ok!!")
	})
	engine.Run()
}

在这里插入图片描述

10.从 reader 读取数据

访问http://localhost:8080/test后,将下载url地址的图片并将其命名为123.png 但现在将其下载却打不开

func main() {
	router := gin.Default()
	router.GET("/test", func(c *gin.Context) {
		response, err := http.Get("https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%BC%A0%E7%B4%AB%E5%AE%81%E5%9B%BE%E7%89%87&hs=0&pn=1&spn=0&di=7136437450519347201&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=2643363613%2C2106712228&os=3028774708%2C1970574687&simid=2643363613%2C2106712228&adpicid=0&lpn=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=0&oriquery=%E5%BC%A0%E7%B4%AB%E5%AE%81%E5%9B%BE%E7%89%87&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fn.sinaimg.cn%2Fsinakd20111%2F539%2Fw1960h2579%2F20210619%2F9e65-74af1bfc3b1873479616e5a37bb490eb.jpg%26refer%3Dhttp%3A%2F%2Fn.sinaimg.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1666405458%26t%3D02eb7dfb10fad465e8d88e94d0074659&fromurl=ippr_z2C%24qAzdH3FAzdH3Fh_z%26e3Bftgw_z%26e3Bv54_z%26e3BvgAzdH3Fw6ptvsj_0n9lnd0n8n_8kma11c18aa8aapj4p_z%26e3Bip4s&gsm=2&islist=&querylist=&dyTabStr=MCwzLDYsMiwxLDQsNSw4LDcsOQ%3D%3D.jpg")
		//	StatusCode 响应体的状态码
		if err != nil || response.StatusCode != http.StatusOK {
			//Status设置HTTP响应码 503
			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="123.png"`,
		}
		// DataFromReader 将指定的读取器写入主体流 并 更新HTTP代码
		c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
	})
	router.Run(":8080")
}

11. BasicAuth 中间件 验证用户登录

func main() {
	r := gin.Default()
	// 路由组使用 gin.BasicAuth() 中间件
	// gin.Accounts 是 map[string]string 的一种快捷方式
	authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
		//用户名 密码
		"foo":    "bar",
		"austin": "1234",
		"lena":   "hello2",
		"manu":   "4321",
	}))

	// /admin/secrets 端点
	// 触发 "localhost:8080/admin/secrets
	authorized.GET("/secrets", func(c *gin.Context) {
		// 获取用户,它是由 BasicAuth 中间件设置的
		//如果给定键存在,MustGet将返回该键的值,否则将出现panic。
		user := c.MustGet(gin.AuthUserKey).(string) //获取输入的用户名
		//secret 对应键名为user的值  也就是用户数据
		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 :("})
		}
	})
	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":8080")
}

12.嵌套路由组

func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()
	authorized := r.Group("/")
	authorized.Use(readEndpoint)
	{
		authorized.POST("/login", loginEndpoint)
	}
	// 嵌套路由组
	//访问 http://localhost:8080/testing/analytics 得到结果
	testing := authorized.Group("testing")
	testing.GET("/analytics", analyticsEndpoint)

	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":8080")
}

13.只绑定 url 查询字符串

ShouldBindQuery 函数只绑定 url 查询参数而忽略 post 数据。

type Person struct {
	Name    string `form:"name"`
	Address string `form:"address"`
}

func main() {
	route := gin.Default()
	//任何都注册一个与所有HTTP方法匹配的路由。获取,发布,放置,修补,头部,选项,删除,连接,跟踪
	route.Any("/testing", startPage)
	route.Run()
}

func startPage(c *gin.Context) {
	var person Person
	//ShouldBindQuery是c.ShouldBindWith(obj, binding.Query)的快捷方式。
	if c.ShouldBindQuery(&person) == nil {
		log.Println("====== Only Bind By Query String ======")
		log.Println(person.Name)
		log.Println(person.Address)
	}
	c.String(200, "Success\n")
	c.JSON(200, gin.H{
		"address": person.Address,
		"name":    person.Name,
	})
}

在这里插入图片描述

14 在中间件中使用 Goroutine

当在中间件或 handler 中启动新的 Goroutine 时,不能使用原始的上下文,必须使用只读副本。

func main() {
	r := gin.Default()

	r.GET("/long_async", func(c *gin.Context) {
		// 创建在 goroutine 中使用的副本
		cCp := c.Copy()
		go func() {
			// 用 time.Sleep() 模拟一个长任务。
			time.Sleep(5 * time.Second)
			// 请注意您使用的是复制的上下文 "cCp",这一点很重要
			log.Println("Done! in path " + cCp.Request.URL.Path)
		}()
	})

	r.GET("/long_sync", func(c *gin.Context) {
		// 用 time.Sleep() 模拟一个长任务。
		time.Sleep(5 * time.Second)

		// 因为没有使用 goroutine,不需要拷贝上下文
		log.Println("Done! in path " + c.Request.URL.Path)
	})

	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":8080")
}

15 如何记录日志

func main() {
	// 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
	gin.DisableConsoleColor()
	// Create 创建一个名为 Winne.log 的文件
	f, _ := os.Create("2.log")
	// DefaultWriter=os.Stdout 指向系统的标准输出
	//将日志记录到文件 f
	gin.DefaultWriter = io.MultiWriter(f)
	// 如果需要同时将日志写入文件和控制台,请使用以下代码。
	//gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
	router := gin.Default()
	router.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})
	router.Run(":8080")
}

16 将 request body 绑定到不同的结构体中

要想多次绑定,可以使用 c.ShouldBindBodyWith. 只有某些格式需要此功能,如 JSON, XML, MsgPack, ProtoBuf。

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{}
  // 读取 c.Request.Body 并将结果存入上下文。
  if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
    c.String(http.StatusOK, `the body should be formA`)
  // 这时, 复用存储在上下文中的 body。
  } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
    c.String(http.StatusOK, `the body should be formB JSON`)
  // 可以接受其他格式
  } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
    c.String(http.StatusOK, `the body should be formB XML`)
  } else {
    ...
  }
}

17 映射查询字符串或表单参数

func main() {
	router := gin.Default()
	router.POST("/post", func(c *gin.Context) {
		ids := c.QueryMap("ids") //url参数
		names := c.PostFormMap("names")//post过来的数据
		fmt.Printf("ids: %v; names: %v", ids, names)
		c.JSON(200, ids)
		c.JSON(200, names)
	})
	router.Run()
}

在这里插入图片描述

18 查询字符串参数 Query DefaultQuery

func main() {
	router := gin.Default()
	// 使用现有的基础请求对象解析查询字符串参数。
	// 示例 URL: /welcome?firstname=winnie&lastname=jine
	router.GET("/welcome", func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname", "Guest")
		lastname := c.Query("lastname") // c.Request.URL.Query().Get("lastname") 的一种快捷方式
		c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
	})
	router.Run(":8080")
}

19 模型绑定和验证

19.1 JSON

// 绑定 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()
	// 绑定 JSON ({"user": "winnie", "password": "123"})
	router.POST("/login", 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 != "winnie" || json.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
		c.JSON(200, json)
	})
	router.Run()
}

在这里插入图片描述

19.2 XML

// 绑定 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()
	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 != "OCEAN" || xml.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
		c.JSON(200, xml)
	})
	router.Run()
}

在这里插入图片描述

19.3 绑定html表单

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()
	// 绑定 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"})
		c.JSON(200, form)
	})
	router.Run()
}

在这里插入图片描述

20 绑定 Uri

type Person struct {
	ID   string `uri:"id" binding:"required"`
	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.Error()})
			return
		}
		c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
	})
	route.Run()
}

在这里插入图片描述

21 绑定查询字符串或表单数据

type Person struct {
	Name     string    `form:"name"`
	Address  string    `form:"address"`
	Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}

func main() {
	route := gin.Default()
	route.GET("/testing", startPage)
	route.Run()
}

func startPage(c *gin.Context) {
	var person Person
	// 如果是 `GET` 请求,只使用 `Form` 绑定引擎(`query`)。
	// 如果是 `POST` 请求,首先检查 `content-type` 是否为 `JSON` 或 `XML`,然后再使用 `Form`(`form-data`)。
	// 查看更多:https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L88
	if c.ShouldBind(&person) == nil {
		log.Println(person.Name)
		log.Println(person.Address)
		log.Println(person.Birthday)
	}
	c.JSON(200, person)
	c.String(200, "Success")
}

在这里插入图片描述

如果 采用post方法
在这里插入图片描述

22 绑定表单数据至自定义结构体

func main() {
	r := gin.Default()
	r.GET("/getb", func(context *gin.Context) {
		var b StructB
		//shouldbind与bind作用一致,但是 shouldbind相比较于bind能更好的控制绑定
		context.ShouldBind(&b)
		context.JSON(200, gin.H{
			"f_a": b.NestedStruct.FieldA,
			"f_b": b.FiledB,
		})
	})
	r.GET("/getc", func(context *gin.Context) {
		var c StructC
		context.ShouldBind(&c)
		context.JSON(200, gin.H{
			//"f_a": (*(c.NestedStructPointer)).FieldA
			"f_a": c.NestedStructPointer.FieldA,
			"f_c": c.FieldC,
		})

	})
	r.GET("/getd", func(context *gin.Context) {
		var d StructD
		context.ShouldBind(&d)
		context.JSON(200, gin.H{
			"f_x": d.NestedAnnoyStruct.FieldX,
			"f_d": d.FieldD,
		})
	})
	r.Run()
}

目前仅支持没有 form 的嵌套结构体 ,例如 下列 不支持以下格式的结构体

type StructX struct {
    X struct {} `form:"name_x"` // 有 form
}

type StructY struct {
    Y StructX `form:"name_y"` // 有 form
}

type StructZ struct {
    Z *StructZ `form:"name_z"` // 有 form
}

23 自定义 HTTP 配置

func main() {
	router := gin.Default()
	router.GET("/", func(context *gin.Context) {
		context.String(200, "自定义配置成功")
	})
	http.ListenAndServe(":8080", router)
}

func main() {
	router := gin.Default()
	router.GET("/", func(context *gin.Context) {
		context.String(200, "配置成功")
	})
	s := &http.Server{
		Addr:           ":9999",
		Handler:        router,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	s.ListenAndServe()
}

24 自定义中间件

func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		// 设置 example 变量
		c.Set("example", "12345")
		// 请求前
		c.Next()
		// 请求后
		// Since返回从 t 开始经过的时间。
		latency := time.Since(t)
		log.Print(latency)
		// 获取发送的 status Status返回当前请求的HTTP响应状态码。
		status := c.Writer.Status()
		log.Println(status)
	}
}

func main() {
	r := gin.New()
	//或者 r.Use(Logger())
	r.GET("/test", Logger(), func(c *gin.Context) {
		//如果给定键存在,MustGet将返回该键的值,
		example := c.MustGet("example").(string)
		// 打印:"12345"
		log.Println(example)
	})
	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":9999")
}

在这里插入图片描述

25 自定义日志文件

func main() {
	router := gin.New()
	// LoggerWithFormatter 中间件会写入日志到 gin.DefaultWriter
	// 默认 gin.DefaultWriter = os.Stdout
	//LoggerWithFormatter实例 是 一个Logger中间件,具有 指定的日志格式功能。
	//LogFormatterParams是任何格式化程序在需要记录日志时都会提交的结构
	router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
		// 你的自定义格式
		return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
			param.ClientIP,
			param.TimeStamp.Format(time.RFC1123),
			param.Method,
			param.Path,
			param.Request.Proto,
			param.StatusCode,
			param.Latency,
			param.Request.UserAgent(),
			param.ErrorMessage,
		)
	}))
	router.Use(gin.Recovery())
	router.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})
	router.Run(":8080")
}

在这里插入图片描述

26 自定义验证器

这个自定义验证器 验证流程有两个要素
一:需要传入两次时间 分别是 checkin与checkout 每传入一次, bookableDate 都需要验证一次
二:gtfield=CheckIn 用来约束 check_out时间 大于 check_in时间。

// Booking 包含绑定和验证的数据。
type Booking struct {
	CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
	// gtfield 是一个默认规则,意思是要大于某个字段的值. gtfield=CheckIn表明 check_out的值要大于check_in的值
	CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"`
}

// 定义一个验证方法,用来验证时间是否合法
// 验证方法返回值应该是个布尔值
//	type Func func(fl FieldLevel) bool
// FieldLevel	包含验证字段的所有信息和帮助函数
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
	// Field 返回当前字段进行验证
	// 将值以 interface{} 类型返回
	date, ok := fl.Field().Interface().(time.Time)
	if ok {
		today := time.Now()
		//判断today是否在date之后 如果是 返回 true,否则返回 false
		if today.After(date) {
			return false
		}
	}
	return true
}

func main() {
	route := gin.Default()
	// Validator 是实现 StructValidator 接口的 默认验证器.
	// Engine 返回为StructValidator实现提供动力 的 底层验证器引擎。
	// Validate 包含 验证器设置和缓存
	// if语句 表示 是否验证器设置成功
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		//注册一个自定义验证方法 bookabledate
		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()})
	}
}

27 设置获取 cookie

func main() {

	router := gin.Default()

	router.GET("/cookie", func(c *gin.Context) {
		// 获取cookie
		cookie, err := c.Cookie("gin_cookie")
		//表示 获取出错
		if err != nil {
			cookie = "NotSet"
			c.SetCookie("gin_cookie", "winnie", 3600, "/", "localhost", false, true)
		}
		fmt.Printf("Cookie value: %s \n", cookie)
	})
	router.Run()
}

在这里插入图片描述

第一次的 Cookie value: NotSet 原因:刚开始没获取到,所以cookie 值设为NotSet,并且重新设置了cookie,因为重新设置后并没有获取cookie,所以 cookie的值仍然是NotSet。输出为 Cookie value: NotSet
第二次 获取到cookie了 所以打印成功。

28 路由参数

func main() {
	router := gin.Default()

	// 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /user
	router.GET("/user/:name", func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "Hello %s", name)
	})

	// 此 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)
	})

	router.Run(":8080")
}

29 一个程序是否可以服务两个端口

下面这个代码是只能运行80端口,不能运行90端口。

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.JSON(200, "我是8080")
	})
	router.Run(":8080") // data services

	routerAdmin := gin.Default()
	routerAdmin.GET("/", func(c *gin.Context) {
		c.JSON(200, "我是8090")
	})
	routerAdmin.Run(":8090") // admin and monitor services
}

在这里插入图片描述
下面这个代码能同时运行80 90端口

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.JSON(200, "我是80端口")
	})
	go router.Run(":8080") // 开启一个携程

	routerAdmin := gin.Default()
	routerAdmin.GET("/", func(c *gin.Context) {
		c.JSON(200, "我是90端口")
	})
	routerAdmin.Run(":8090") // admin and monitor services
}

在这里插入图片描述
gin文档示例代码

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()
	})
	// Wait 等待阻塞,直到所有来自Go方法的函数调用都返回,然后从它们返回第一个非nil错误(如果有的话)。
	if err := g.Wait(); err != nil {
		log.Fatal(err)
	}
}

30

31

32

33

34

35

36

37

38

39

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值