1.请求

1.1参数相关

1.查询参数(Query String)

来源:URL 中 ? 后面的键值对

注意:

  • 自动解析,无需关心 Content-Type

  • 空格/中文会被浏览器 URL-encode,Gin 自动 decode

// 查询参数
func query(c *gin.Context) {

    //单值
	fmt.Println(c.Query("user"))
	fmt.Println(c.GetQuery("user"))
    //多值
	fmt.Println(c.QueryArray("user"))
    //默认值
	fmt.Println(c.DefaultQuery("addr", "beijing"))
}

2.动态参数(Router Parameter )

来源:路由规则里写死的“占位符”

注意:

  • 名字必须和路由定义完全一致,区分大小写

  • 不支持“可选”占位符,必须段段对应

// 动态参数
func param(c *gin.Context) {
	fmt.Println(c.Param("user"))
	fmt.Println(c.Param("book"))
}

r.GET("/param/:user/:book", param)

3.表单参数(Form / PostForm)

来源:
application/x-www-form-urlencoded(普通表单)
multipart/form-data(带文件上传)

注意:

  • 只能出现在 POST/PUT/PATCH 且 Body 未被读走;一旦 c.ShouldBindJSON 读了 Body,后续再读 Form 会为空

  • 文件上传别忘了 enctype="multipart/form-data"

// 表单参数
func form(c *gin.Context) {

    //单值
	fmt.Println(c.PostForm("name"))
    //多值
	fmt.Println(c.PostFormArray("name"))
    //默认值
	fmt.Println(c.DefaultPostForm("addr", "default"))
    //文件
    file, _ := c.FormFile("avatar")          // 单文件
    files := c.MultipartForm()               // 多文件
}

4.原始参数(Raw Body,字节流)

来源:客户端把 JSON/XML/二进制直接写在 Body 里

注意:

  • c.Request.Bodyio.ReadCloser,只能读一次;读完就空了

  • 若想多次使用,需要先 c.Set("rawBody", bodyBytes) 存起来,或自己封装一个 TeeReader

  • 常见 Content-Type:application/json, application/xml, text/plain, application/octet-stream

// 使用GetRawData
func raw(c *gin.Context) {
	type Person struct {
		Name string `json:"name"`
		Age  int    `json:"age"`
	}
	var person Person
	err := bindJSON(c, &person)
	if err != nil {
		fmt.Printf(err.Error())
	}
	fmt.Println(person)
}

func bindJSON(c *gin.Context, obj any) error {

	data, _ := c.GetRawData()

	//立即把 body 写回去,后续逻辑还能读
	c.Request.Body = io.NopCloser(bytes.NewBuffer(data))

	contentType := c.GetHeader("Content-Type")
	switch contentType {
	case "application/json":
		err := json.Unmarshal(data, obj)
		if err != nil {
			fmt.Printf(err.Error())
			return err
		}
    case xxx: 
           xxxxxxxxxxx
    default:
           xxxxxxxxxxx
	}
    
	return nil
}


------------------------------------------------------
//不使用GetRawData

// 一次性读成字节
bodyBytes, _ := io.ReadAll(c.Request.Body)

// 常见:绑定到结构体
var user User
if err := c.ShouldBindJSON(&user); err != nil { … }

1.2请求方式

方法 Gin 注册函数 典型用途 幂等* 安全* 有无 Body
GET r.GET() 读资源(查询)
POST r.POST() 新建资源
PUT r.PUT() 整表替换/更新
PATCH r.PATCH() 局部更新
DELETE r.DELETE() 删除资源 可有可无
HEAD r.HEAD() 只拿响应头
OPTIONS r.OPTIONS() CORS 预检 可有可无
ANY r.Any() 一次性捕获全部方法

*幂等:重复执行结果不变;安全:不修改服务器数据。

读取 GET,新增 POST,整改 PUT,局部 PATCH,删除 DELETE,探活 HEAD,预检 OPTIONS,调试通吃 Any。

type Article struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}
type Result[T any] struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data T      `json:"data"`
}

func main() {
	r := gin.Default()
	r.GET("/articles", getList)             //查所有
	r.GET("/articles/:id", getDetail)       //查单个
	r.POST("/articles", create)             //增
	r.PUT("/articles/:id", update)          //改
	r.DELETE("/articles/:id", remove)       //删

	r.Run(":8080")
}

func getList(c *gin.Context) {
	articleList := []Article{
		{"Go语言入门", "这篇文章是《Go语言入门》"},
		{"python语言入门", "这篇文章是《python语言入门》"},
		{"JavaScript语言入门", "这篇文章是《JavaScript语言入门》"},
	}
	c.JSON(200, Result[[]Article]{200, "success", articleList})
}

func getDetail(c *gin.Context) {
	fmt.Println(c.Param("id"))
	article := Article{"Go语言入门", "这篇文章是《Go语言入门》"}
	c.JSON(200, Result[Article]{200, "success", article})
}

func create(c *gin.Context) {
	var article Article
	err := bindJSON(c, &article)
	if err != nil {
		c.JSON(400, Result[string]{400, "解析错误", err.Error()})
		return
	}
	c.JSON(200, Result[Article]{200, "success", article})
}

func update(c *gin.Context) {
	fmt.Println(c.Param("id"))
	var article Article
	err := bindJSON(c, &article)
	if err != nil {
		c.JSON(400, Result[string]{400, "解析错误", err.Error()})
		return
	}
	c.JSON(200, Result[Article]{200, "success", article})
}

func remove(c *gin.Context) {
	fmt.Println(c.Param("id"))
	c.JSON(200, Result[string]{0, "success", "删除成功"})
}
func bindJSON(c *gin.Context, obj any) error {
	data, _ := c.GetRawData()
	contentType := c.GetHeader("Content-Type")
	switch contentType {
	case "application/json":
		err := json.Unmarshal(data, &obj)
		if err != nil {
			fmt.Printf(err.Error())
			return err
		}
	}
	return nil
}

1.3请求头

请求头(Request Header)是浏览器 / 客户端在 HTTP 请求行之后、Body 之前 发送的一组 键值对,用来告诉服务器“我是谁、我要什么、我能接受什么”。

1.字段介绍

字段 作用 典型值 / 示例
Host 目标域名+端口 api.example.com
User-Agent 客户端软件信息 Mozilla/5.0 ...
Accept 期望响应格式 application/json
Accept-Language 期望语言 zh-CN,en;q=0.9
Accept-Encoding 期望压缩算法 gzip, deflate, br
Content-Type 请求 Body 格式 application/json
Content-Length Body 字节数 123
Authorization 身份令牌 Bearer <token> / Basic <base64>
Cookie 浏览器自动带上的键值对 sessionid=abc
Origin 浏览器跨域请求的来源域 https://spa.example.com
Referer 从哪个页面跳转过来 https://google.com
X-Request-ID 链路追踪唯一号 uuid-abc-123
X-Forwarded-For 经过代理后的真实客户端 IP 118.123.3.22, 10.0.0.1
If-Modified-Since 缓存时间戳 Wed, 21 Oct 2015 07:28:00 GMT

Host 找主机,User-Agent 报身份,Accept 谈格式,Authorization 带令牌,Cookie 维持会话,X-Forwarded-For 追真 IP。

2.请求头获取

请求头的获取可以获取单个请求头(c.GetHeader(key))

                             获取所有请求头(c.Request.Header)

                             获取原始请求头字符串(c.Request.Header.Get(key)    //使用少)

func main() {
	r := gin.Default()
	//请求头
	r.GET("/request", func(c *gin.Context) {

        //此种方式获取的时候对,字母大小写不区分
		fmt.Println(c.GetHeader("User-Agent"))
		fmt.Println(c.GetHeader("user-Agent"))
		fmt.Println(c.GetHeader("user-agEnT"))
		fmt.Println(c.GetHeader("user-agent"))

		fmt.Println(c.Request.Header)

        //此方式获取的时候,使用Get获取的时候不区分大小写,使用map获取的时候区分大小写
		fmt.Println(c.Request.Header.Get("User-Agent"))
		fmt.Println(c.Request.Header["User-Agent"])
		fmt.Println(c.Request.Header["user-Agent"])
		fmt.Println(c.Request.Header.Get("Token"))
		fmt.Println(c.Request.Header.Get("token"))
		fmt.Println(c.Request.Header["Token"])
		fmt.Println(c.Request.Header["token"])
		c.JSON(200, gin.H{"msg": "success"})
	})
	
	r.Run(":8080")
}

2.响应

2.1响应状态码

类别 英文 Gin 常量 一句话场景
成功 200 OK http.StatusOK 正常返回数据
201 Created http.StatusCreated POST 新建成功,返回新资源
204 No Content http.StatusNoContent 删除/更新成功,但无 Body
重定向 301 Moved Permanently http.StatusMovedPermanently 永久跳转,SEO 用
302 Found http.StatusFound 临时跳转,登录后回首页
304 Not Modified http.StatusNotModified 资源未改,走缓存
客户端错 400 Bad Request http.StatusBadRequest 参数格式不对
401 Unauthorized http.StatusUnauthorized 未登录/令牌失效
403 Forbidden http.StatusForbidden 登录了但无权限
404 Not Found http.StatusNotFound 资源不存在
服务端错 500 Internal Server Error http.StatusInternalServerError 代码 panic/未知异常

2xx 成功,3xx 跳转,4xx 客户端错误,5xx 服务端错误

2.2响应数据类型

1.返回字符串

r.GET("/txt",func(c *gin.Context){
	c.String(200,"hello world")
})

2.返回json

r.GET("/json", func(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"message": "hello world",
		"status":  200,
	})
})

r.GET("/moreJson", func(c *gin.Context) {
	type Msg struct {
		Name    string `json:"name"`
		Message string
		Number  int
	}
	msg := Msg{
		Name:    "小王子",
		Message: "hello world",
		Number:  123,
	}
	c.JSON(http.StatusOK, msg)
})

3.返回xml

r.GET("/xml", func(c *gin.Context) {
	c.XML(200, gin.H{
		"message": "pong",
		"status":  "ok",
	})
})

4.返回yaml

r.GET("/yml", func(c *gin.Context) {
	c.YAML(200, gin.H{
		"message": "pong",
		"status":  "ok",
	})
})

5.返回html

//导入模板
r.LoadHTMLGlob("Gin/templates/*")
//定义接口
r.GET("/html", func(c *gin.Context) {
    //index.html为自己导入的模板的名字,gin.H{"name": "xiaobai"}为自己传输的数据
	c.HTML(http.StatusOK, "index.html", gin.H{"name": "xiaobai"})
})

2.3文件响应

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

    //静态文件托管
	//单个文件,前者为访问路径,后者为文件路径
	r.StaticFile("/test", "Gin/static/test.png")
	//多个文件,前者为访问路径的前缀(后面跟具体的文件名),后者为文件路径
	r.StaticFS("/static", http.Dir("Gin/static/static"))
	r.Run(":8080")
}

2.4重定向

场景 Gin 方法 HTTP 码 浏览器表现
临时跳转(默认) c.Redirect(http.StatusFound, "/new") 302 地址栏会变,下次还访问原 URL
永久跳转 c.Redirect(http.StatusMovedPermanently, "/new") 301 地址栏会变,浏览器缓存永久指向新地址
跳外部网址 c.Redirect(302, "https://github.com/gin-gonic/gin") 302 同 302,可站外
内部转发(服务端) c.Request.URL.Path = "/new"
r.HandleContext(c)
200 浏览器无感知,URL 不变

重定向用 Redirect,301 永久 302 临时

func main() {
	r := gin.Default()
	r.GET("/redirect", func(c *gin.Context) {
		c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com/")
	})
	r.Run(":8080")
}

2.5响应头

字段 作用 典型值 / 示例
Content-Type Body 格式 application/json; charset=utf-8
Content-Disposition 下载时指定文件名/方式 attachment; filename="report.pdf"
Access-Control-Allow-Origin CORS 白名单 * 或 https://spa.com
Access-Control-Allow-Methods CORS 允许方法 GET, POST, PUT, DELETE
Access-Control-Allow-Headers CORS 允许头 Content-Type, Authorization
Cache-Control 缓存策略 no-cachemax-age=3600
Location 配合 3xx 重定向 /new-path
Set-Cookie 种 Cookie session=abc; Path=/; HttpOnly
X-Content-Type-Options 防 MIME 嗅探 nosniff
X-Frame-Options 防点击劫持 DENY

先写Header再写Body,下载加 Disposition,跨域加 CORS,缓存加 Cache-Control

r.GET("/response", func(c *gin.Context) {
	c.Header("Token", "6SmNfK3z9bG5iT8vL0nP4qR7tH1uJ2wX3yZ6")
	c.Header("Content-Type", "application/text; charset=utf-8")
	c.JSON(200, gin.H{"data": "看看响应头"})
})
Logo

更多推荐