Gin框架操作指南06:POST绑定(下)

官方文档地址(中文):https://gin-gonic.com/zh-cn/docs/

注:没用过Gin的读者强烈建议 先阅读第一节:Gin操作指南:开山篇

本节继续演示POST绑定,包括将request-body绑定到不同的结构体中;映射查询字符串或表单参数;上传文件 Query和post-form。

目录

一、将request-body绑定到不同的结构体中

go 复制代码
package main

import (
	"net/http"

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

// 定义表单结构体 formA,包含一个必需字段 Foo
type formA struct {
	Foo string `json:"foo" xml:"foo" binding:"required"` // 绑定 JSON 和 XML 的字段 foo
}

// 定义表单结构体 formB,包含一个必需字段 Bar
type formB struct {
	Bar string `json:"bar" xml:"bar" binding:"required"` // 绑定 JSON 和 XML 的字段 bar
}

// 处理请求的函数
func SomeHandler(c *gin.Context) {
	objA := formA{} // 创建 formA 的实例
	objB := formB{} // 创建 formB 的实例

	// c.ShouldBind 使用了 c.Request.Body,不可重用。
	if errA := c.ShouldBind(&objA); errA == nil {
		// 如果绑定成功,返回表单A的成功信息
		c.String(http.StatusOK, `the body should be formA`)
	} else if errB := c.ShouldBind(&objB); errB == nil {
		// 因为现在 c.Request.Body 是 EOF,所以这里会报错。
		c.String(http.StatusOK, `the body should be formB`)
	} else {
		// 处理绑定错误
		c.String(http.StatusBadRequest, `Invalid input`)
	}
}

func main() {
	router := gin.Default()
	router.POST("/some-handler", SomeHandler) // 注册路由
	router.Run(":8080")                       // 启动服务器
}

注意if分支中的注释,效果如下

要想多次绑定,可以使用 c.ShouldBindBodyWith.

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 {

...

}

}

c.ShouldBindBodyWith 会在绑定之前将 body 存储到上下文中。 这会对性能造成轻微影响,如果调用一次就能完成绑定的话,那就不要用这个方法。

只有某些格式需要此功能,如 JSON, XML, MsgPack, ProtoBuf。 对于其他格式, 如 Query, Form, FormPost, FormMultipart 可以多次调用 c.ShouldBind() 而不会造成任任何性能损失 (详见 #1341)。

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

go 复制代码
package main

import (
	"fmt" // 导入格式化I/O库

	"github.com/gin-gonic/gin" // 导入Gin框架
)

func main() {
	router := gin.Default() // 创建默认的Gin路由

	// 设置处理POST请求的路由
	router.POST("/post", func(c *gin.Context) {

		// c.QueryMap("ids") 用于映射查询字符串中的 "ids" 参数
		// 例如:POST /post?ids[a]=1234&ids[b]=hello
		// 这个方法会将查询参数转换为一个map,key为参数名,value为参数值
		ids := c.QueryMap("ids")

		// c.PostFormMap("names") 用于映射表单数据中的 "names" 参数
		// 例如:names[first]=thinkerou&names[second]=tianou
		// 这个方法会将表单数据转换为一个map
		names := c.PostFormMap("names")

		// 打印解析后的 ids 和 names 的值
		// ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]
		fmt.Printf("ids: %v; names: %v", ids, names)
	})

	router.Run(":8080") // 启动服务器并监听8080端口
}

打开postman,输入http://localhost:8080/post?ids[a]=1234&ids[b]=hello

然后在body中添加key和value,注意names[]作为一个key,效果如图

三、上传文件

在"上传文件",目录下建立两个文件夹demo01和demo02,demo01用于单文件上传:

go 复制代码
package main

import (
	"fmt"
	"log"
	"net/http"

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

func main() {
	router := gin.Default()
	// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
	// 将内存限制设置为 8 MiB,这意味着如果上传的文件超过这个大小,
	// 将会返回错误,确保服务器不会因为大文件上传而耗尽内存
	router.MaxMultipartMemory = 8 << 20 // 8 MiB

	// 定义一个 POST 路由,处理文件上传
	router.POST("/upload", func(c *gin.Context) {
		// 从请求中获取文件,"file" 是表单字段的名称
		file, err := c.FormFile("file")
		if err != nil {
			// 处理文件获取错误,返回状态码 400 和错误信息
			c.String(http.StatusBadRequest, "Error retrieving the file")
			return
		}

		// 打印文件名到服务器日志
		log.Println(file.Filename)

		// 设置文件保存的目标路径,文件将保存在当前目录下
		dst := "./" + file.Filename
		// 上传文件至指定的完整文件路径
		if err := c.SaveUploadedFile(file, dst); err != nil {
			// 如果文件保存失败,返回状态码 500 和错误信息
			c.String(http.StatusInternalServerError, "Unable to save the file")
			return
		}

		// 返回成功信息,告知用户文件已上传
		c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
	})

	// 启动服务器,监听 8080 端口
	router.Run(":8080")
}

打开postman,输入http://loaclhost:8080/upload,在body中填充form-data,key=file,类型为File,然后选择文件,选好后点击send,效果如下


多文件上传

go 复制代码
package main

import (
	"fmt"
	"log"
	"net/http"

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

func main() {
	router := gin.Default()
	// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
	// 将内存限制设置为 8 MiB,这意味着如果上传的文件总大小超过这个限制,
	// 将返回错误,确保服务器不会因为大文件上传而耗尽内存
	router.MaxMultipartMemory = 8 << 20 // 8 MiB

	// 定义一个 POST 路由,用于处理文件上传
	router.POST("/upload", func(c *gin.Context) {
		// Multipart form
		// 从请求中获取 multipart 表单数据
		form, err := c.MultipartForm()
		if err != nil {
			// 如果获取 multipart 表单数据失败,返回状态码 400 和错误信息
			c.String(http.StatusBadRequest, "Failed to get multipart form")
			return
		}

		// 获取上传的文件,"upload[]" 是表单字段的名称
		files := form.File["upload[]"]

		// 遍历每个文件
		for _, file := range files {
			// 打印文件名到服务器日志
			log.Println(file.Filename)

			// 设置文件保存的目标路径,文件将保存在当前目录下
			dst := "./" + file.Filename
			// 上传文件至指定目录
			if err := c.SaveUploadedFile(file, dst); err != nil {
				// 如果文件保存失败,返回状态码 500 和错误信息
				c.String(http.StatusInternalServerError, "Unable to save the file")
				return
			}
		}

		// 返回成功信息,告知用户上传的文件数量
		c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
	})

	// 启动服务器,监听 8080 端口
	router.Run(":8080")
}

四、Query和post-form

go 复制代码
package main

import (
	"fmt"

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

func main() {
	// 创建一个新的 Gin 路由引擎
	router := gin.Default()

	// 定义一个 POST 路由,用于处理 POST 请求
	router.POST("/post", func(c *gin.Context) {
		// 从查询字符串中获取 "id" 参数
		id := c.Query("id")
		// 从查询字符串中获取 "page" 参数,如果未提供,则返回默认值 "0"
		page := c.DefaultQuery("page", "0")
		// 从 POST 请求的表单数据中获取 "name" 参数
		name := c.PostForm("name")
		// 从 POST 请求的表单数据中获取 "message" 参数
		message := c.PostForm("message")

		// 打印获取到的参数值到标准输出
		fmt.Printf("id: %s; page: %s; name: %s; message: %s\n", id, page, name, message)
	})

	// 启动 HTTP 服务器,监听 8080 端口
	router.Run(":8080")
}

效果

相关推荐
007php0071 小时前
60分钟熟悉正则表达式
java·学习·mysql·架构·golang·正则表达式·php
GoppViper2 小时前
golang从http请求中读取xml格式的body,并转成json
xml·后端·http·golang
是jin奥2 小时前
Golang 逃逸分析(Escape Analysis)理解与实践篇
开发语言·后端·golang
技术卷3 小时前
Gin框架操作指南03:HTML渲染
golang·gin
The_tuber_sadness4 小时前
【Flutter】- go_router路由
javascript·flutter·golang
qq_172805594 小时前
GO Practise
开发语言·后端·golang·go
tatasix8 小时前
深入解析缓存技术
后端·缓存·golang
王中阳Go9 小时前
止步阿里一面。。。
开发语言·后端·面试·golang
Muroidea1 天前
K8s环境下使用sidecar模式对EMQX的exhook.proto 进行流量代理
云原生·容器·golang·kubernetes·emqx·sidecar·exhook