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 执行完毕")
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)