使用Gin框架接收和处理JSON数据和图像的方法
Golang小白一枚,正在不断学习积累知识,现将学习到的知识记录一下,也是将我的所得分享给大家!而今天这篇文章《使用Gin框架接收和处理JSON数据和图像的方法》带大家来了解一下##content_title##,希望对大家的知识积累有所帮助,从而弥补自己的不足,助力实战开发!
我有请求处理程序的代码:
func (h *handlers) updateprofile() gin.handlerfunc { type request struct { username string `json:"username" binding:"required,min=4,max=20"` description string `json:"description" binding:"required,max=100"` } return func(c *gin.context) { var updaterequest request if err := c.bindjson(&updaterequest); err != nil { var validationerrors validator.validationerrors if errors.as(err, &validationerrors) { validateerrors := base.bindingerror(validationerrors) c.abortwithstatusjson(http.statusbadrequest, gin.h{"error": validateerrors}) } else { c.abortwitherror(http.statusbadrequest, err) } return } avatar, err := c.formfile("avatar") if err != nil { c.abortwithstatusjson(http.statusbadrequest, gin.h{ "error": "image not contains in request", }) return } log.print(avatar) if avatar.size > 3<<20 { // if avatar size more than 3mb c.abortwithstatusjson(http.statusbadrequest, gin.h{ "error": "image is too large", }) return } file, err := avatar.open() if err != nil { c.abortwitherror(http.statusinternalservererror, err) } session := sessions.default(c) id := session.get("sessionid") log.printf("id type: %t", id) err = h.userservice.updateprofile(fmt.sprintf("%v", id), file, updaterequest.username, updaterequest.description) if err != nil { c.abortwithstatusjson(http.statusbadrequest, gin.h{}) return } c.indentedjson(http.statusnocontent, gin.h{"message": "succesfull update"}) } }
我对此处理程序进行了单元测试:
func testuser_updateprofile(t *testing.t) { type testcase struct { name string image io.reader username string description string expectedstatuscode int } router := gin.default() memstore := memstore.newstore([]byte("secret")) router.use(sessions.sessions("session", memstore)) usergroup := router.group("user") repo := user.newmemory() service := userservice.new(repo) userhandlers.register(usergroup, service) testimage := make([]byte, 100) rand.read(testimage) image := bytes.newreader(testimage) testcases := []testcase{ { name: "request with image", image: image, username: "bobik", description: "wanna be sharik", expectedstatuscode: http.statusnocontent, }, { name: "request without image", image: nil, username: "sharik", description: "wanna be bobik", expectedstatuscode: http.statusnocontent, }, } for _, tc := range testcases { t.run(tc.name, func(t *testing.t) { body := &bytes.buffer{} writer := multipart.newwriter(body) imagewriter, err := writer.createformfile("avatar", "test_avatar.jpg") if err != nil { t.fatal(err) } if _, err := io.copy(imagewriter, image); err != nil { t.fatal(err) } data := map[string]interface{}{ "username": tc.username, "description": tc.description, } jsondata, err := json.marshal(data) if err != nil { t.fatal(err) } jsonwriter, err := writer.createformfield("json") if err != nil { t.fatal(err) } if _, err := jsonwriter.write(jsondata); err != nil { t.fatal(err) } writer.close() // creating request req := httptest.newrequest( http.methodpost, "http://localhost:8080/user/account/updateprofile", body, ) req.header.set("content-type", writer.formdatacontenttype()) log.print(req) w := httptest.newrecorder() router.servehttp(w, req) assert.equal(t, tc.expectedstatuscode, w.result().statuscode) }) } }
在测试过程中出现以下错误: 错误#01:数字文字中的无效字符“-”
这是请求正文(我用 log.print(req) 打印它):
&{POST http://localhost:8080/user/account/updateprofile HTTP/1.1 1 1 map[Content-Type:[multipart/form-data; boundary=30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035]] {--30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035 Content-Disposition: form-data; name="avatar"; filename="test_avatar.jpg" Content-Type: application/octet-stream --30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035 Content-Disposition: form-data; name="json" {"description":"wanna be bobik","username":"sharik"} --30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035-- }414 [] false localhost:8080 map[] map[] map[] 192.0.2.1:1234 http://localhost:8080/user/account/updateprofile }
首先,我只有字符串作为 json 数据并将其转换为字节。当出现错误时,我使用 json.marshal 转换了 json 数据,但没有成功。我想用 c.bind 解析 json 数据并用 c.formfile 解析给定图像,这可能吗?
更新。我替换了代码先获取头像,然后通过bind结构获取json。现在我有 eof 错误。
正确答案
tl;dr
我们可以定义一个结构体来同时接收json数据和图像文件(注意字段标签):
var updaterequest struct { avatar *multipart.fileheader `form:"avatar" binding:"required"` user struct { username string `json:"username" binding:"required,min=4,max=20"` description string `json:"description" binding:"required,max=100"` } `form:"user" binding:"required"` } // c.shouldbind will choose binding.formmultipart based on the content-type header. // we call c.shouldbindwith to make it explicitly. if err := c.shouldbindwith(&updaterequest, binding.formmultipart); err != nil { _ = c.abortwitherror(http.statusbadrequest, err) return }
gin可以自动解析multipart/form-data
中的其他内容类型吗?
例如,xml
或 yaml
。
当前的 gin (@1.9.0) 不会自动解析 multipart/form-data
中的 xml
或 yaml
。 json
很幸运,因为当目标字段是结构体或映射时,gin 恰好使用 json.unmarshal
解析表单字段值。请参阅 binding.setwithpropertype。
我们可以像这样自己解析它们(updaterequest.event
是表单中的字符串值):
var event struct { at time.time `xml:"time" binding:"required"` player string `xml:"player" binding:"required"` action string `xml:"action" binding:"required"` } if err := binding.xml.bindbody([]byte(updaterequest.event), &event); err != nil { _ = c.abortwitherror(http.statusbadrequest, err) return }
(请不要与 application/xml
请求中的 yaml
或 application/x-yaml
请求中的 xml
混淆。仅当 xml
内容或 yaml
内容位于 中时才需要这样做多部分/表单-data
请求) .
其他
c.bindjson
不能用于从multipart/form-data
读取 json,因为它假定请求正文以有效的 json 开头。但它是从一个边界开始的,看起来像--30b24345d...
。这就是为什么它失败并显示错误消息invalid character '-' in numeric literal
。- 在
c.formfile("avatar")
之后调用c.bindjson
不起作用,因为调用c.formfile
会使整个请求正文被读取。并且c.bindjson
后面没有什么可读的。这就是您看到 eof 错误的原因。
单个可运行文件中的演示
这是完整的演示。使用 go test 运行 ./... -v -count 1
:
package m import ( "bytes" "crypto/rand" "fmt" "io" "mime/multipart" "net/http" "net/http/httptest" "testing" "time" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/stretchr/testify/assert" ) func handle(c *gin.Context) { var updateRequest struct { Avatar *multipart.FileHeader `form:"avatar" binding:"required"` User struct { Username string `json:"username" binding:"required,min=4,max=20"` Description string `json:"description" binding:"required,max=100"` } `form:"user" binding:"required"` Event string `form:"event" binding:"required"` } // c.ShouldBind will choose binding.FormMultipart based on the Content-Type header. // We call c.ShouldBindWith to make it explicitly. if err := c.ShouldBindWith(&updateRequest, binding.FormMultipart); err != nil { _ = c.AbortWithError(http.StatusBadRequest, err) return } fmt.Printf("%#v\n", updateRequest) var event struct { At time.Time `xml:"time" binding:"required"` Player string `xml:"player" binding:"required"` Action string `xml:"action" binding:"required"` } if err := binding.XML.BindBody([]byte(updateRequest.Event), &event); err != nil { _ = c.AbortWithError(http.StatusBadRequest, err) return } fmt.Printf("%#v\n", event) } func TestMultipartForm(t *testing.T) { testImage := make([]byte, 100) if _, err := rand.Read(testImage); err != nil { t.Fatal(err) } image := bytes.NewReader(testImage) body := &bytes.Buffer{} writer := multipart.NewWriter(body) imageWriter, err := writer.CreateFormFile("avatar", "test_avatar.jpg") if err != nil { t.Fatal(err) } if _, err := io.Copy(imageWriter, image); err != nil { t.Fatal(err) } if err := writer.WriteField("user", `{"username":"bobik","description":"wanna be sharik"}`); err != nil { t.Fatal(err) } xmlBody := `` if err := writer.WriteField("event", xmlBody); err != nil { t.Fatal(err) } writer.Close() req := httptest.NewRequest( http.MethodPost, "http://localhost:8080/update", body, ) req.Header.Set("Content-Type", writer.FormDataContentType()) fmt.Printf("%v\n", req) w := httptest.NewRecorder() c, engine := gin.CreateTestContext(w) engine.POST("/update", handle) c.Request = req engine.HandleContext(c) assert.Equal(t, 200, w.Result().StatusCode) } playerOne strike (miss)
感谢您的阅读!
本篇关于《使用Gin框架接收和处理JSON数据和图像的方法》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于Golang的相关知识,请关注golang学习网公众号!

- 上一篇
- Azure JWT 认证在 Go 中无效

- 下一篇
- 在扫描 SQLC 生成的代码时,使用 LEFT 连接查询时遇到 NULL 列导致出错
-
- Golang · Go问答 | 1年前 |
- 在读取缓冲通道中的内容之前退出
- 139浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 戈兰岛的全球 GOPRIVATE 设置
- 204浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何将结构作为参数传递给 xml-rpc
- 325浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何用golang获得小数点以下两位长度?
- 477浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何通过 client-go 和 golang 检索 Kubernetes 指标
- 486浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 将多个“参数”映射到单个可变参数的习惯用法
- 439浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 将 HTTP 响应正文写入文件后出现 EOF 错误
- 357浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 结构中映射的匿名列表的“复合文字中缺少类型”
- 352浏览 收藏
-
- Golang · Go问答 | 1年前 |
- NATS Jetstream 的性能
- 101浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何将复杂的字符串输入转换为mapstring?
- 440浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 相当于GoLang中Java将Object作为方法参数传递
- 212浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何确保所有 goroutine 在没有 time.Sleep 的情况下终止?
- 143浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 协启动
- SEO摘要协启动(XieQiDong Chatbot)是由深圳协启动传媒有限公司运营的AI智能服务平台,提供多模型支持的对话服务、文档处理和图像生成工具,旨在提升用户内容创作与信息处理效率。平台支持订阅制付费,适合个人及企业用户,满足日常聊天、文案生成、学习辅助等需求。
- 9次使用
-
- Brev AI
- 探索Brev AI,一个无需注册即可免费使用的AI音乐创作平台,提供多功能工具如音乐生成、去人声、歌词创作等,适用于内容创作、商业配乐和个人创作,满足您的音乐需求。
- 9次使用
-
- AI音乐实验室
- AI音乐实验室(https://www.aimusiclab.cn/)是一款专注于AI音乐创作的平台,提供从作曲到分轨的全流程工具,降低音乐创作门槛。免费与付费结合,适用于音乐爱好者、独立音乐人及内容创作者,助力提升创作效率。
- 9次使用
-
- PixPro
- SEO摘要PixPro是一款专注于网页端AI图像处理的平台,提供高效、多功能的图像处理解决方案。通过AI擦除、扩图、抠图、裁切和压缩等功能,PixPro帮助开发者和企业实现“上传即处理”的智能化升级,适用于电商、社交媒体等高频图像处理场景。了解更多PixPro的核心功能和应用案例,提升您的图像处理效率。
- 9次使用
-
- EasyMusic
- EasyMusic.ai是一款面向全场景音乐创作需求的AI音乐生成平台,提供“零门槛创作 专业级输出”的服务。无论你是内容创作者、音乐人、游戏开发者还是教育工作者,都能通过EasyMusic.ai快速生成高品质音乐,满足短视频、游戏、广告、教育等多元需求。平台支持一键生成与深度定制,积累了超10万创作者,生成超100万首音乐作品,用户满意度达99%。
- 12次使用
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- Golang取得代码运行时间的问题
- 2023-02-24 501浏览
-
- 请问 go 代码如何实现在代码改动后不需要Ctrl+c,然后重新 go run *.go 文件?
- 2023-01-08 501浏览
-
- 如何从同一个 io.Reader 读取多次
- 2023-04-11 501浏览