使用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--
} <nil> 414 [] false localhost:8080 map[] map[] <nil> map[] 192.0.2.1:1234 http://localhost:8080/user/account/updateprofile <nil> <nil> <nil> <nil>}
首先,我只有字符串作为 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 := `<?xml version="1.0" encoding="UTF-8"?>
<root>
<time>2023-02-14T19:04:12Z</time>
<player>playerOne</player>
<action>strike (miss)</action>
</root>`
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)
}
感谢您的阅读!
本篇关于《使用Gin框架接收和处理JSON数据和图像的方法》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于Golang的相关知识,请关注golang学习网公众号!
Azure JWT 认证在 Go 中无效
- 上一篇
- 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获得小数点以下两位长度?
- 478浏览 收藏
-
- 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基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3176次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3388次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3417次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4522次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3796次使用
-
- 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浏览

