गो में JSON पोस्ट अनुरोध को संभालना


250

इसलिए मेरे पास निम्नलिखित है, जो अविश्वसनीय रूप से हैकरी लगता है, और मैं अपने आप से सोच रहा हूं कि गो ने इससे बेहतर डिज़ाइन किए गए पुस्तकालय हैं, लेकिन मुझे JSON डेटा के POST अनुरोध को संभालने वाले गो का उदाहरण नहीं मिल सकता है। वे सभी POST हैं।

यहाँ एक उदाहरण अनुरोध है: curl -X POST -d "{\"test\": \"that\"}" http://localhost:8082/test

और यहाँ कोड लॉग्स के साथ कोड है:

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))
}

वहाँ एक बेहतर तरीका है, है ना? मैं सिर्फ यह जानकर स्तब्ध हूं कि सबसे अच्छा अभ्यास क्या हो सकता है।

(गो को खोज इंजनों के रूप में गोलंग के रूप में भी जाना जाता है, और यहां उल्लेख किया गया है ताकि अन्य इसे पा सकें।)


3
यदि आप उपयोग करते हैं curl -X POST -H 'Content-Type: application/json' -d "{\"test\": \"that\"}", तो req.Form["test"]वापस आना चाहिए"that"
विनीसियस

@Vinicius क्या इसके कोई प्रमाण हैं?
दिरलिक १५'१

जवाबों:


389

के json.Decoderबजाय का उपयोग करें 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
क्या आप कृपया बता सकते हैं कि क्यों?
रयान बिग बिग

86
शुरुआत के लिए, ऐसा लगता है कि यह एक धारा को संभालने के बजाय आपको यह सब अपने आप को एक बफर में लोड करने की आवश्यकता है। (मैं एक अलग जो बीटीडब्ल्यू) हूं
जो 14

7
मुझे आश्चर्य है कि इस मामले में उचित त्रुटि हैंडलिंग कैसे दिखेगी। मुझे नहीं लगता कि अमान्य जोंस से घबराना अच्छा है।
कोडपश्र

15
मुझे नहीं लगता कि आपको defer req.Body.Close()डॉक्स से: "सर्वर अनुरोध निकाय को बंद कर देगा। ServeHTTP हैंडलर की आवश्यकता नहीं है।" डॉक्स से @thisisnotabus का जवाब देने के लिए: "सर्वर अनुरोधों के लिए अनुरोध निकाय हमेशा गैर-शून्य होता है, लेकिन कोई भी बॉडी मौजूद नहीं होने पर तुरंत EOF वापस आ जाएगा" golang.org/pkg/net/http/#ttequest
ड्र्यू लेसेर

22
मैं सुझाव दूंगा कि उपयोग न करें json.Decoder। यह JSON ऑब्जेक्ट्स की धाराओं के लिए अभिप्रेत है, एक वस्तु नहीं। यह एक JSON ऑब्जेक्ट के लिए अधिक कुशल नहीं है क्योंकि यह संपूर्ण ऑब्जेक्ट को मेमोरी में पढ़ता है। इसमें नकारात्मक पक्ष यह है कि यदि कचरा वस्तु के बाद शामिल किया जाता है तो वह शिकायत नहीं करेगा। कुछ कारकों के आधार पर, json.Decoderशरीर को पूरी तरह से नहीं पढ़ा जा सकता है और कनेक्शन पुन: उपयोग के लिए अयोग्य होगा।
काले बी

85

से पढ़ने की जरूरत है req.BodyParseFormविधि से पढ़ रही है req.Bodyऔर उसके बाद मानक HTTP एन्कोडेड स्वरूप में पार्स करने। आप जो चाहते हैं वह शरीर को पढ़ना है और इसे JSON प्रारूप में पार्स करना है।

यहां आपका कोड अपडेट किया गया है।

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))
}

धन्यवाद! मैं देखता हूं कि अब मैं कहां गलत हो रहा था। अगर तुम बुलाओreq.ParseForm() , जो मैं इस समस्या को हल करने के लिए, इससे पहले कि आप कोशिश करते हैं और पढ़ कोशिश कर के पहले प्रयास में क्या कर रहा था req.Body, यह पता शरीर स्पष्ट करने के लिए लगता है और unexpected end of JSON inputफेंक दिया जाता है जब आप के पास जाओ Unmarshal(कम से कम 1.0.2 में)
TomJ

1
@ डैनियल: जब मैं कर्ल -एक्स POST -d "{\" tes \ ": \" that \ "}" localhost: 8082 / test , log.rintln (t.Test) खाली करता हूं। क्यों ? या उस मामले के लिए यदि कोई अन्य JSON पोस्ट करता है, तो वह खाली हो जाता है
सोमेश

आपका POST अनुरोध गलत है। tes! = परीक्षा। सराहना करते हैं कि 5 साल पहले था: /
रामबेटिनो

यह एक अच्छा सरल उदाहरण है!
15412s

यह अच्छी सलाह है, लेकिन स्पष्ट होने के लिए, उपयोग के संदर्भ में उत्तर json.NewDecoder(req.Body)भी सही हैं।
रिच

59

मैं इस सटीक समस्या से खुद को पागल कर रहा था। मेरे JSON मार्शल और Unmarshaller मेरी गो संरचना को आबाद नहीं कर रहे थे। तब मुझे इसका समाधान https://eager.io/blog/go-and-json पर मिला :

"गो में सभी स्ट्रक्चर्स के साथ, यह याद रखना महत्वपूर्ण है कि जेन्स मार्शल जैसे बाहरी कार्यक्रमों के लिए केवल पहले कैपिटल वाले अक्षर ही दिखाई देते हैं।"

उसके बाद, मेरे मार्शल और अनमरशैलर ने पूरी तरह से काम किया!


कृपया लिंक से कुछ स्निपेट शामिल करें। यदि यह पदावनत हो जाता है तो उदाहरण खो जाएंगे।
0:30

47

दो कारणों से अधिक क्यों json.Decoderपसंद किया जाना चाहिए json.Unmarshal- यह 2013 से सबसे लोकप्रिय जवाब में संबोधित नहीं किया गया है:

  1. फरवरी 2018, go 1.10एक नई विधि json.Decoder.DisallowUnognFields () शुरू की पेश किया जो अवांछित JSON- इनपुट का पता लगाने की चिंता को संबोधित करता है
  2. req.Bodyपहले से ही एक है io.Reader। इसकी संपूर्ण सामग्री को पढ़ना और फिर json.Unmarshalअपशिष्ट संसाधनों का प्रदर्शन अगर धारा थी, तो अमान्य JSON के 10MB ब्लॉक का कहना है। अनुरोध निकाय को पार्स करने के साथ json.Decoder, जैसा कि यह स्ट्रीम करता है, यदि अमान्य JSON का सामना किया गया था, तो एक प्रारंभिक पार्स त्रुटि को ट्रिगर करेगा। वास्तविक समय में I / O स्ट्रीम को संसाधित करना पसंदीदा तरीका है

खराब उपयोगकर्ता इनपुट का पता लगाने के बारे में कुछ उपयोगकर्ता टिप्पणियों को संबोधित करते हुए:

अनिवार्य क्षेत्र और अन्य स्वच्छता जांचों को लागू करने के लिए, प्रयास करें:

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)

खेल का मैदान

विशिष्ट उत्पादन:

$ 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
केवल यह बताते हुए कि कुछ बुरा है, इसके बारे में राय
बताने के

क्या आप जानते हैं कि यह क्या संभाल नहीं करता है? मैंने देखा कि टेस्ट दो बार
जुबान

@ tooptoop4 एक को डुप्लिकेट फ़ील्ड के बारे में चेतावनी देने के लिए एक कस्टम डिकोडर लिखने की आवश्यकता होगी - डिकोडर में अक्षमता को जोड़ना - एक परिदृश्य को संभालने के लिए सभी जो कभी नहीं होगा। कोई मानक JSON एनकोडर कभी भी डुप्लिकेट फ़ील्ड का उत्पादन नहीं करेगा।
colm.anseo

20

मुझे डॉक्स से निम्नलिखित उदाहरण वास्तव में मददगार लगे ( यहाँ स्रोत )।

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)
    }
}

यहाँ महत्वपूर्ण यह है कि ओपी को डिकोड होना था

type test_struct struct {
    Test string
}

... किस स्थिति में हम ड्रॉप करेंगे const jsonStream, और Messageसंरचना को इसके साथ बदलें test_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)
    }
}

अपडेट : मैं यह भी जोड़ना चाहूंगा कि यह पोस्ट JSON के साथ भी प्रतिक्रिया देने के बारे में कुछ महान डेटा प्रदान करता है। लेखक बताते हैं struct tags, जिसके बारे में मुझे जानकारी नहीं थी।

चूँकि JSON सामान्य रूप से नहीं दिखता है {"Test": "test", "SomeKey": "SomeVal"}, बल्कि {"test": "test", "somekey": "some value"}आप इस तरह अपनी संरचना का पुनर्गठन कर सकते हैं:

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

... और अब आपका हैंडलर "SomeKey" (जो आप आंतरिक रूप से उपयोग कर रहे हैं) के विपरीत "कुछ-की" का उपयोग करके JSON को पार्स करेगा।


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.