php小编百草为您介绍Golang gin框架中如何接收JSON数据和图像。在开发过程中,我们经常需要处理前端传递过来的JSON数据以及图像文件。Golang的gin框架提供了简单易用的方法来接收和处理这些数据。通过本文的介绍,您将了解到如何在gin框架中使用结构体来接收JSON数据,以及如何处理上传的图像文件。让我们一起来探索吧!
我有请求处理程序的代码:
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 错误。
我们可以定义一个结构体来同时接收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 }
multipart/form-data
中的其他内容类型吗?例如,xml
或 yaml
。
当前的 gin (@1.9.0) 不会自动解析 multipart/form-data
中的 xml
或 yaml
。 json
很幸运,因为当目标字段是结构体或映射时,gin 恰好使用 json.unmarshal
中的 。 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.bindjson
不能用于从 --30b24345d...
。这就是为什么它失败并显示错误消息 invalid character '-' in numeric literal
。c.formfile("avatar")
之后调用 c.bindjson
不起作用,因为调用 c.formfile
会使整个请求正文被读取。并且 c.bindjson
在
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) }
以上是Golang gin接收json数据和图像的详细内容。更多信息请关注PHP中文网其他相关文章!