gin框架中的Context源码解读

package main

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

func main() {
	r := gin.Default()
	// 如果不设置代理,默认信任所有的代理
	_ = r.SetTrustedProxies([]string{"127.0.0.1"})

	r.POST("/index/:name/:age", MyMiddleWare(), MyMiddleWare02(), index)
	r.POST("/home", func(context *gin.Context) {
		context.String(201, "home页面")
	})

	r.Run("127.0.0.1:8080")
}

func index(context *gin.Context) {
	// 1. context中获取不同类型的值
	//name := context.GetBool("name")
	//now := context.GetTime("current")

	// 2. 从url中取参数(位置参数)
	//name := context.Param("name")
	//fmt.Println(name)

	// 3. 从查询字符串中取参数(关键字参数), context.Query()如果没有直接返回空字符串,而后面的原生查询,没有就报下标错了
	//name, age := context.Query("name"), context.Request.URL.Query()["age"][0]
	//fmt.Println(name, age)

	// 4. DefaultQuery跟Query类似,不过,没有值的时候会返回默认值
	//name := context.DefaultQuery("name", "wagnwu")
	//age := context.DefaultQuery("age", "18")
	//gender := context.DefaultQuery("gender", "nan")
	//fmt.Println(name, age, gender)

	// 5. GetQuery
	//name, ok := context.GetQuery("name")
	//fmt.Println(name, ok)

	// 6. QueryArray, 获取查询字符串切片
	//hobbies := context.QueryArray("hobbies")
	//fmt.Println(hobbies)

	// 7. GetQueryArray跟QueryArray只是多返回了一个布尔类型,表名键存在与否

	// 8. GetQueryMap  请求方式: http://127.0.0.1:8080/index/abs?user[name]=张三&user[age]=25
	//m, ok := context.GetQueryMap("user")
	//fmt.Println(m, ok)

	// 9. QueryMap, 调用了GetQueryMap

	// PostForm支持两种格式:multipart-form、form-url-encoded
	// 10. PostFormArray=>[]string
	//hobbies := context.PostFormArray("hobbies")
	//fmt.Println(hobbies)

	// 11. PostForm=>string
	//hobby := context.PostForm("hobby")
	//fmt.Println(hobby)

	// 12. DefaultPostForm=> string

	// 13. PostFormMap  // 请求格式user[name]=王五, user[age]=28
	//m := context.PostFormMap("user")
	//fmt.Println(m)

	// 14. FormFile=>上传单个文件
	//fh, _ := context.FormFile("file")
	//if err := context.SaveUploadedFile(fh, fmt.Sprintf("./http_server/file/%s", fh.Filename)); err != nil {
	//	log.Println(err.Error())
	//}

	// 15. MultipartForm=>上传多个文件
	//mul, err := context.MultipartForm()
	//if err != nil {
	//	fmt.Println(err)
	//}
	//fmt.Println(mul.Value["name"])  // 可以同时传递值和文件
	//fmt.Println(mul.File["file"])
	//fhs := mul.File["file"]
	//for _, fh := range fhs{
	//	if err = context.SaveUploadedFile(fh, fmt.Sprintf("http_server/file/%s", fh.Filename)); err != nil {
	//		log.Println("上传文件失败")
	//	}
	//}

	// 16. Bind
	/*
	func (c *Context) Bind(obj interface{}) error {
		// Bind方法其实是其它所有方法的总和,因为它通过请求方法和请求类型,动态获取Binding类型
		// 然后调用MustBindWith跟其它的都一致
		b := binding.Default(c.Request.Method, c.ContentType())
		return c.MustBindWith(obj, b)
	}
	*/
	//var u User
	//if err := context.Bind(&u); err != nil {
	//	log.Println(err.Error())
	//}
	//fmt.Println(u)

	/* Bind不能自动解析的其它的Bind
		BindQuery解析成Form
		BindHeader需要单独调用这个方法
		BindUri也需要单独调用
	 */

	// Bind家族和ShouldBind家族的两点区别:
	// 这两个家族都调用ShouldBindWith方法,如果该方法出现错误
	// Bind家族会将状态码设置为400,终止后续处理链,返回错误
	// 而ShouldBind家族,只会返回不错
	// context.ShouldBindBodyWith(&u, binding.JSON)
	// 当在一个请求的处理链中,如果不只是绑定了大于1次的话,可以考虑用这个,因为它会把读取正文的字节切片放到上下文中
	// 如果在该请求的处理链中,只绑定了一个,那么应该使用ShouldBindWith,以获得更好的性能

	// 17. ClientIP=>采用了一种尽力查找真实客户端ip的行为
	//ip := context.ClientIP()
	//fmt.Println(ip)

	// 18. IsWebsocket
	//ok := context.IsWebsocket()
	//fmt.Println(ok)

	// 19. 写入响应码
	//context.Status(http.StatusNotFound)
	//context.Writer.Write([]byte("hello"))

	// 20. 添加响应头
	//context.Header("Connection", "upgrade")
	//context.Header("Upgrade", "websocket")
	//context.Header("Connection", "")  // 移除响应头

	// 21. 获取请求头
	//contentType := context.GetHeader("Content-Type")
	//fmt.Println(contentType)

	// 22. 获取原生数据
	//byt, _ := context.GetRawData()
	//fmt.Println(string(byt))

	// 23. samesite
	//context.SetSameSite(4)

	// 24. 设置cookie, maxAge: 单位秒
	//context.SetCookie("session", "123456", 1000, "", "127.0.0.1",true, true)

	// 25. 获取cookie
	//cook, _ := context.Cookie("session")
	//fmt.Println(cook)

	// 26. Render
	//context.Status(http.StatusOK)
	//render.WriteJSON(context.Writer, "abc")

	// 27. 输出漂亮的json, 开发使用,生产坚决不要使用(消耗性能)

	// 28. String
	//context.String(200, "hello %s world", "哈哈哈")

	// 29. Redirect, 注意;重定向的location对应的方法与当前请求的方法需要一致
	//context.Redirect(http.StatusPermanentRedirect, "/home")

	// 30. 自定义返回数据类型
	//context.Data(200, "application/json", []byte("hello world"))

	// 31. 返回文件内容
	//context.File("http_server/file/02.png")

	// 32. 客户端以给定的文件名下载文件
	//context.FileAttachment("http_server/file/01.png", "abc.png")

	// 33. 根据Offered的类型,自动返回对应的格式数据,如果对应的该格式数据为nil,那么就会返回Data里面的数据,很强
	//context.Negotiate(200, gin.Negotiate{
	//	Offered:  []string{context.ContentType()},
	//	HTMLName: "",
	//	HTMLData: nil,
	//	JSONData: gin.H{"avatar_url": "http://mayanan.cn"},
	//	XMLData:  gin.H{"score": 88},
	//	YAMLData: gin.H{"gender": true},
	//	Data:     gin.H{"name": "马雅涵", "age": 38},
	//})

	// 34. Bind,根据前端传递过来的Content-Type,自动绑定对应的Binding类型
	var user User
	if err := context.Bind(&user); err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(user)

	context.JSON(http.StatusOK, gin.H{"msg": "OK"})
}

// User 注意:json和yaml格式不需要额外写标签,自动转换,form和xml格式需要额外写上标签,否则只能是大写的字段
// Bind方法可以自动解析下面常用格式的数据,当然还有protobuf、msgpack也可以自动解析,这里没有列举
// post请求如下,(注意:如果是get请求,全部走formBinding中的处理)
// json
/*
{
	"name": "马123",
	"age": 35
}
 */
// multipart-form, 只能在form中写
/*
name abc  age 123
 */
// form-url-encoded, 既可以在form表单中写,也可以在url查询字符串中写,form中优先于url的key
/*
?name=abc&age=456
 */
// yaml
/*
name: add
age: 85
 */
// 注意:yaml的header是application/x-yaml
// xml
/*
<xml>
	<name>wangwu</name>
	<age>29</age>
</xml>
 */
type User struct {
	Name string `form:"name" xml:"name" uri:"name"`
	Age int `form:"age" xml:"age" uri:"age"`
}

func MyMiddleWare() gin.HandlerFunc{
	return func(context *gin.Context) {
		// 1. 停止后续处理链,并返回响应码
		// context.AbortWithStatus(401)

		// 2. 停止后续处理链,返回响应码和json响应体
		//context.AbortWithStatusJSON(200, gin.H{"msg": "OK"})

		// 3. 调用AbortWithStatus, 返回一个*gin.Error错误
		//err := context.AbortWithError(200, &gin.Error{Err: errors.New("这是一个错误")})

		// 4. Error往context.Errors上下文错误切片中追加错误
		//_ = context.Error(errors.New("这是一个错误"))
		//fmt.Println(err)
		//fmt.Println(context.Errors)

		// 5. 上下文中设置键值对
		//context.Set("name", "wangwu")  // 注意Set中因为map是并发不安全的,写的时候加了互斥锁 c.Lock()
		//fmt.Println("-------MyMiddleWare01 开始执行")
		//context.Next()
		//fmt.Println("========MyMiddleWare01, 执行完毕")

	}
}

func MyMiddleWare02() gin.HandlerFunc {
	return func(context *gin.Context) {
		//fmt.Println("-------MyMiddleWare02, 开始执行")
		//fmt.Println(context.GetString("name"), "MyMiddleWare02, 获取到的name")
		//context.Set("current", time.Now())
		//context.Next()
		//fmt.Println("=======MyMiddleWare02 执行完毕")
	}
}

posted @ 2022-05-22 09:28  专职  阅读(415)  评论(0编辑  收藏  举报