editor php Baicao memperkenalkan kepada anda cara menerima data dan imej JSON dalam rangka kerja gin Golang. Semasa proses pembangunan, kami selalunya perlu memproses data JSON dan fail imej yang dihantar dari bahagian hadapan. Rangka kerja gin Golang menyediakan kaedah yang mudah dan mudah digunakan untuk menerima dan memproses data ini. Melalui pengenalan artikel ini, anda akan belajar cara menggunakan struktur dalam rangka kerja gin untuk menerima data JSON dan cara memproses fail imej yang dimuat naik. Mari kita meneroka bersama-sama!
Saya ada kod untuk pengendali permintaan:
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"}) } }
Saya unit menguji pengendali ini:
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) }) } }
Ralat berikut berlaku semasa ujian: Ralat #01: Aksara '-' tidak sah dalam literal angka
Ini ialah badan permintaan (saya mencetaknya menggunakan 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>}
Pertama, saya hanya mempunyai rentetan sebagai data json dan menukarnya kepada bait. Apabila ralat berlaku, saya menukar data json menggunakan json.marshal tanpa berjaya. Saya ingin menghuraikan data json menggunakan c.bind dan imej yang diberikan menggunakan c.formfile, adakah ini mungkin?
Dikemas kini. Saya menggantikan kod untuk mendapatkan avatar terlebih dahulu dan kemudian mendapatkan json melalui struktur bind. Sekarang saya mempunyai kesilapan eof.
Kami boleh menentukan struktur untuk menerima data json dan fail imej (perhatikan label medan):
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
? Sebagai contoh, xml
或 yaml
.
Gin semasa (@1.9.0) tidak menyelesaikan secara automatik multipart/form-data
中的 xml
或 yaml
。 json
很幸运,因为当目标字段是结构体或映射时,gin 恰好使用 json.unmarshal
dalam . json
bertuah kerana gin menggunakan json.unmarshal
untuk menghuraikan nilai medan bentuk apabila medan sasaran ialah struct atau peta. Lihat binding.setwithpropertype
updaterequest.event
Kita boleh menghuraikannya sendiri seperti ini (
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
(sila jangan request dengan ) .c.bindjson
不能用于从 multipart/form-data
读取 json,因为它假定请求正文以有效的 json 开头。但它是从一个边界开始的,看起来像 --30b24345d...
。这就是为什么它失败并显示错误消息 invalid character '-' in numeric literal
c.bindjson
tidak boleh digunakan untuk membaca json daripada --30b24345d...
. Itulah sebabnya ia gagal dengan mesej ralat aksara tidak sah '-' dalam literal angka
. c.formfile("avatar")
之后调用 c.bindjson
不起作用,因为调用 c.formfile
会使整个请求正文被读取。并且 c.bindjson
Tiada apa-apa untuk dibaca selepas
go test 运行 ./... -v -count 1
Ini adalah demo penuh. Gunakan
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) }
Atas ialah kandungan terperinci Golang gin menerima data dan imej json. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!