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
解析表單欄位值。請參閱 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) }
感謝您的閱讀!
以上是Golang gin接收json資料和影像的詳細內容。更多資訊請關注PHP中文網其他相關文章!