Golang gin menerima data dan imej json

PHPz
Lepaskan: 2024-02-09 13:09:17
ke hadapan
551 orang telah melayarinya

Golang gin接收json数据和图像

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!

Kandungan soalan

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"})
    }
}
Salin selepas log masuk

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)
        })
    }
}
Salin selepas log masuk

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>}
Salin selepas log masuk

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.

Penyelesaian

tl;dr

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
}
Salin selepas log masuk
Bolehkah

gin menghuraikan jenis kandungan lain secara automatik dalam multipart/form-data?

Sebagai contoh, xmlyaml.

Gin semasa (@1.9.0) tidak menyelesaikan secara automatik multipart/form-data 中的 xmlyamljson 很幸运,因为当目标字段是结构体或映射时,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.eventKita boleh menghuraikannya sendiri seperti ini (

ialah nilai rentetan dalam bentuk):

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
}
Salin selepas log masuk
application/xml 请求中的 yamlapplication/x-yaml 请求中的 xml 混淆。仅当 xml 内容或 yaml 内容位于 中时才需要这样做多部分/表单-data (sila jangan request dengan

) .

Lain-lain
  1. c.bindjson 不能用于从 multipart/form-data 读取 json,因为它假定请求正文以有效的 json 开头。但它是从一个边界开始的,看起来像 --30b24345d...。这就是为什么它失败并显示错误消息 invalid character '-' in numeric literalc.bindjson tidak boleh digunakan untuk membaca json daripada
  2. kerana ia mengandaikan bahawa badan permintaan bermula dengan json yang sah. Tetapi ia bermula pada sempadan yang kelihatan seperti --30b24345d.... Itulah sebabnya ia gagal dengan mesej ralat aksara tidak sah '-' dalam literal angka.
  3. c.formfile("avatar") 之后调用 c.bindjson 不起作用,因为调用 c.formfile 会使整个请求正文被读取。并且 c.bindjsonTiada apa-apa untuk dibaca selepas
  4. . Itulah sebabnya anda melihat ralat eof.

Demo dalam satu fail boleh jalan

go test 运行 ./... -v -count 1Ini 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)
}
Salin selepas log masuk
Terima kasih kerana membaca! 🎜

Atas ialah kandungan terperinci Golang gin menerima data dan imej json. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!