Go'da JSON Post İsteğini İşleme


250

Bu yüzden, inanılmaz derecede acımasız görünen aşağıdakilere sahibim ve Go'nun kendisinden bundan daha iyi tasarlanmış kütüphaneler olduğunu düşünüyorum, ancak Go'nun JSON verilerinin POST isteğini işleyişine bir örnek bulamıyorum. Hepsi POST'lardır.

İşte bir örnek istek: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test

Ve işte günlükler gömülü kod:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type test_struct struct {
    Test string
}

func test(rw http.ResponseWriter, req *http.Request) {
    req.ParseForm()
    log.Println(req.Form)
    //LOG: map[{"test": "that"}:[]]
    var t test_struct
    for key, _ := range req.Form {
        log.Println(key)
        //LOG: {"test": "that"}
        err := json.Unmarshal([]byte(key), &t)
        if err != nil {
            log.Println(err.Error())
        }
    }
    log.Println(t.Test)
    //LOG: that
}

func main() {
    http.HandleFunc("/test", test)
    log.Fatal(http.ListenAndServe(":8082", nil))
}

Daha iyi bir yol olmalı, değil mi? En iyi uygulamanın ne olabileceğini bulmak beni çok şaşırttı.

(Go, arama motorlarına Golang olarak da bilinir ve burada bu şekilde bahsedilmiştir, böylece diğerleri bulabilir.)


3
Eğer kullanırsanız curl -X POST -H 'Content-Type: application/json' -d "{\"test\": \"that\"}", o zaman req.Form["test"]dönmelidir"that"
Vinicius

@Vinicius Bunun bir kanıtı var mı?
diralik

Yanıtlar:


389

Lütfen json.Decoderyerine kullanın json.Unmarshal.

func test(rw http.ResponseWriter, req *http.Request) {
    decoder := json.NewDecoder(req.Body)
    var t test_struct
    err := decoder.Decode(&t)
    if err != nil {
        panic(err)
    }
    log.Println(t.Test)
}

79
Nedenini açıklar mısınız?
Ryan Bigg

86
Başlangıç ​​olarak, bunun bir akışı kendiniz bir tampon belleğe yüklemenize gerek duymak yerine bir akışı işleyebileceği anlaşılıyor. (Ben farklı bir Joe BTW)
Joe

7
Bu durumda doğru hata işlemenin nasıl olacağını merak ediyorum. Geçersiz bir json'a panik yapmanın iyi bir fikir olduğunu düşünmüyorum.
codepushr

15
Yapmanız gerekeceğini sanmıyorum defer req.Body.Close()Belgelerden: "Sunucu istek gövdesini kapatır. ServeHTTP İşleyicisinin buna gerek yoktur." Ayrıca @thisisnotabus'a şu belgelerden cevap vermek için: "Sunucu istekleri için İstek Gövdesi her zaman sıfır değildir, ancak hiçbir gövde olmadığında EOF'u
Drew LeSueur

22
Kullanmamanızı öneririm json.Decoder. Tek bir nesne için değil, JSON nesnelerinin akışları için tasarlanmıştır. Tüm nesneyi belleğe okuduğundan tek bir JSON nesnesi için daha verimli değildir. Çöp nesneden sonra dahil edilirse şikayet etmeyeceği bir dezavantajı vardır. Birkaç faktöre bağlı olarak json.Decoder, vücudu tam olarak okuyamayabilir ve bağlantı yeniden kullanım için uygun olmayacaktır.
Kale B

85

Okumanız gerekiyor req.Body. ParseFormYöntem dan okuyor req.Bodystandart HTTP kodlanmış biçiminde ayrıştırma sonra ve. İstediğiniz, gövdeyi okumak ve JSON biçiminde ayrıştırmaktır.

Kodunuz güncellendi.

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "io/ioutil"
)

type test_struct struct {
    Test string
}

func test(rw http.ResponseWriter, req *http.Request) {
    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        panic(err)
    }
    log.Println(string(body))
    var t test_struct
    err = json.Unmarshal(body, &t)
    if err != nil {
        panic(err)
    }
    log.Println(t.Test)
}

func main() {
    http.HandleFunc("/test", test)
    log.Fatal(http.ListenAndServe(":8082", nil))
}

Teşekkürler! Şimdi nerede yanlış gittiğimi görüyorum. req.ParseForm()Bu sorunu çözmeye çalışmak için yaptığım daha önceki girişimlerde yaptığımı ararsanız , okumaya ve denemeden önce req.Bodyvücudu temizler gibi görünür ve (en azından unexpected end of JSON inputUnmarshal
1.0.2'de

1
@Daniel: -X POST -d "{\" tes \ ": \" that \ "}" curl yaptığımda localhost: 8082 / test , log.Println (t.Test) boş döner. Neden ? Veya başka bir JSON gönderirse bu konuda boş döner
Somesh

POST isteğiniz yanlış. tes! = test. Takdir etmek 5 yıl önce: /
Rambatino

Bu güzel ve basit bir örnek!
15412s

Bu iyi bir tavsiye, ancak açık olmak gerekirse, kullanımına ilişkin cevaplar json.NewDecoder(req.Body)da doğrudur.
Zengin

59

Bu sorunla kendimi delirtiyordum. JSON Marshaller ve Unmarshaller, Go yapımı doldurmuyorlardı. Sonra çözümü https://eager.io/blog/go-and-json adresinde buldum :

"Go'daki tüm yapılarda olduğu gibi, JSON Mareşaller gibi harici programlarda yalnızca büyük harf ilk harfi olan alanların görünür olduğunu hatırlamak önemlidir."

Bundan sonra, Marshaller ve Unmarshaller mükemmel çalıştı!


Lütfen bağlantıdan bazı snippet'ler ekleyin. Kullanımdan kaldırılırsa örnekler kaybolacaktır.
030

47

Neden json.Decodertercih edilmesinin iki nedeni var json.Unmarshal- bunlar 2013'ten gelen en popüler cevapta ele alınmadı:

  1. Şubat 2018, json.Decoder.DisallowUnknownFields ()go 1.10 yöntemini kullanarak istenmeyen JSON girdisini algılama endişesini giderdi
  2. req.Bodyzaten bir io.Reader. Tüm içeriğinin okunması ve ardından json.Unmarshalakış olması durumunda atık kaynakları gerçekleştirilmesi , 10 MB'lık geçersiz bir JSON bloğu olduğunu varsayalım. İstek gövdesinin ayrıştırılması, geçersiz bir JSON ile karşılaşılması durumunda akıştajson.Decoder olduğu gibi erken ayrıştırma hatasını tetikler. G / Ç akışlarının gerçek zamanlı olarak işlenmesi tercih edilen gidiş yoludur .

Hatalı kullanıcı girdisini algılama ile ilgili bazı kullanıcı yorumlarını ele alma:

Zorunlu alanları ve diğer sağlık kontrollerini zorunlu kılmak için şunları deneyin:

d := json.NewDecoder(req.Body)
d.DisallowUnknownFields() // catch unwanted fields

// anonymous struct type: handy for one-time use
t := struct {
    Test *string `json:"test"` // pointer so we can test for field absence
}{}

err := d.Decode(&t)
if err != nil {
    // bad JSON or unrecognized json field
    http.Error(rw, err.Error(), http.StatusBadRequest)
    return
}

if t.Test == nil {
    http.Error(rw, "missing field 'test' from JSON object", http.StatusBadRequest)
    return
}

// optional extra check
if d.More() {
    http.Error(rw, "extraneous data after JSON object", http.StatusBadRequest)
    return
}

// got the input we expected: no more, no less
log.Println(*t.Test)

Oyun alanı

Tipik çıktı:

$ curl -X POST -d "{}" http://localhost:8082/strict_test

expected json field 'test'

$ curl -X POST -d "{\"Test\":\"maybe?\",\"Unwanted\":\"1\"}" http://localhost:8082/strict_test

json: unknown field "Unwanted"

$ curl -X POST -d "{\"Test\":\"oops\"}g4rB4g3@#$%^&*" http://localhost:8082/strict_test

extraneous data after JSON

$ curl -X POST -d "{\"Test\":\"Works\"}" http://localhost:8082/strict_test 

log: 2019/03/07 16:03:13 Works

6
Sadece bir şeyin kötü olduğunu belirtmek yerine fikirlerinizi açıkladığınız için teşekkür ederiz
Fjolnir Dvorak

neyin işe yaramadığını biliyor musun? Test iki kez json olabilir gördüm ve 2. oluşumu kabul
tooptoop4

@ tooptoop4 yinelenen alanlar hakkında uyarmak için özel bir kod çözücü yazmanız gerekir. Hiçbir standart JSON kodlayıcı yinelenen alanlar üretmez.
colm.anseo

20

Aşağıdaki örnek dokümanlar gerçekten yararlı buldum (kaynak burada ).

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "log"
    "strings"
)

func main() {
    const jsonStream = `
        {"Name": "Ed", "Text": "Knock knock."}
        {"Name": "Sam", "Text": "Who's there?"}
        {"Name": "Ed", "Text": "Go fmt."}
        {"Name": "Sam", "Text": "Go fmt who?"}
        {"Name": "Ed", "Text": "Go fmt yourself!"}
    `
    type Message struct {
        Name, Text string
    }
    dec := json.NewDecoder(strings.NewReader(jsonStream))
    for {
        var m Message
        if err := dec.Decode(&m); err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%s: %s\n", m.Name, m.Text)
    }
}

Buradaki anahtar OP'nin kodunu çözmek istediğidir

type test_struct struct {
    Test string
}

... bu durumda yapıyı bırakıp const jsonStreamyerine aşağıdakileri koyacağız :Messagetest_struct

func test(rw http.ResponseWriter, req *http.Request) {
    dec := json.NewDecoder(req.Body)
    for {
        var t test_struct
        if err := dec.Decode(&t); err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        }
        log.Printf("%s\n", t.Test)
    }
}

Güncelleme : Bu yazının JSON ile de yanıt verme konusunda bazı harika veriler sağladığını da ekleyeceğim . Yazar struct tags, farkında olmadığımı açıklıyor .

JSON yana normalde gibi görünmüyor {"Test": "test", "SomeKey": "SomeVal"}, aksine {"test": "test", "somekey": "some value"}, bu gibi yapı yeniden yapılandırabilir:

type test_struct struct {
    Test string `json:"test"`
    SomeKey string `json:"some-key"`
}

... ve işleyiciniz JSON'u "SomeKey" ("dahili olarak kullanacaksınız" yerine "bazı anahtar" kullanarak ayrıştıracak).


1
type test struct {
    Test string `json:"test"`
}

func test(w http.ResponseWriter, req *http.Request) {
    var t test_struct

    body, _ := ioutil.ReadAll(req.Body)
    json.Unmarshal(body, &t)

    fmt.Println(t)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.