Go에서 JSON 포스트 요청 처리


250

그래서 나는 다음을 가지고 있습니다. 매우 해키처럼 보이고 Go는 이것보다 라이브러리를 더 잘 설계했다고 생각했지만 Go는 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))
}

더 좋은 방법이 있어야합니까? 모범 사례가 무엇인지 찾아내는 데 어려움을 겪었습니다.

Go는 검색 엔진에 Golang이라고도하며 여기에서 언급되어 다른 사람들이 찾을 수 있습니다.


3
를 사용하는 curl -X POST -H 'Content-Type: application/json' -d "{\"test\": \"that\"}"경우 다음 req.Form["test"]을 반환해야합니다"that"
Vinicius

@Vinicius 이것에 대한 증거가 있습니까?
diralik

답변:


388

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
이유를 설명해 주시겠습니까?
Ryan Bigg

86
시작하려면 스트림에 버퍼를 직접로드하지 않고 스트림을 처리 할 수 ​​있습니다. (나는 다른 Joe BTW입니다)
Joe

7
이 경우 적절한 오류 처리가 어떻게 보일지 궁금합니다. 잘못된 JSON에 당황하는 것이 좋은 생각은 아닙니다.
codepushr

15
나는 당신이 필요가 있다고 생각하지 않습니다 defer req.Body.Close()워드 프로세서에서 : "서버가 요청 본문을 닫을 것 ServeHTTP 처리기가 필요하지 않습니다.." 또한 문서에서 @thisisnotabus에 대답하기 위해 : "서버 요청의 경우 요청 본문은 항상 0이 아니지만 본문이 없으면 즉시 EOF를 반환합니다" golang.org/pkg/net/http/#Request
Drew LeSueur

22
사용하지 않는 것이 좋습니다 json.Decoder. 단일 객체가 아닌 JSON 객체의 스트림을위한 것입니다. 단일 JSON 객체는 전체 객체를 메모리로 읽기 때문에 더 효율적이지 않습니다. 객체 뒤에 쓰레기가 포함되어 있으면 불평하지 않는다는 단점이 있습니다. 몇 가지 요인에 따라 json.Decoder본문을 완전히 읽지 못할 수 있으며 연결을 재사용 할 수 없게됩니다.
Kale B

85

에서 읽어야 req.Body합니다. 이 ParseForm메소드는 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본문을 지우고 (적어도 1.0.2에서) unexpected end of JSON input갈 때 던져집니다.Unmarshal
TomJ

1
@Daniel : curl -X POST -d "{\"tes \ ": \"that \ "}" localhost : 8082 / test 할 때 log.Println (t.Test)는 비어 있습니다. 왜 ? 다른 JSON을 게시하는 경우 또는 그 문제에 관해서, 그것은 빈 반환
Somesh

POST 요청이 잘못되었습니다. tes! = 테스트. 5 년 전의 감사 : /
Rambatino

이것은 좋은 간단한 예입니다!
15412

이것은 좋은 조언이지만, 명확하게 말해서, 사용법을 언급 한 답변 json.NewDecoder(req.Body)도 정확합니다.
리치

59

나는이 정확한 문제로 나를 미치게했다. 내 JSON Marshaller와 Unmarshaller가 내 Go 구조체를 채우지 않았습니다. 그런 다음 https://eager.io/blog/go-and-json 에서 해결책을 찾았습니다 .

"Go의 모든 구조체와 마찬가지로 첫 글자가 대문자 인 필드 만 JSON Marshaller와 같은 외부 프로그램에서 볼 수 있다는 것을 기억하는 것이 중요합니다."

그 후 내 Marshaller와 Unmarshaller가 완벽하게 작동했습니다!


링크의 일부 스 니펫을 포함하십시오. 더 이상 사용되지 않으면 예제가 손실됩니다.
030

47

json.Decoder선호해야하는 두 가지 이유 가 있습니다 json.Unmarshal-2013 년의 가장 인기있는 답변에서 다루지 않습니다 :

  1. 2018 년 2 월, 원하지 않는 JSON 입력 감지 문제를 해결 go 1.10하는 새로운 메소드 json.Decoder.DisallowUnknownFields () 가 도입되었습니다.
  2. req.Body이미입니다 io.Reader. json.Unmarshal스트림이 유효하지 않은 경우 전체 콘텐츠를 읽은 다음 수행 하면 리소스가 낭비됩니다 (예 : 10MB의 잘못된 JSON 블록). 와, 요청 본문을 구문 분석 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
그냥 뭔가를 주장하는 대신 의견을 설명 주셔서 감사 나쁘다
Fjolnir 드보르작

그것이 처리하지 않는 것을 알고 있습니까? 테스트가 json에 두 번있을 수 있고 2 번째 발생을 허용 함
tooptoop4

@ tooptoop4는 중복 필드에 대해 경고하기 위해 사용자 정의 디코더를 작성해야합니다-디코더에 비 효율성을 추가하는 것은 결코 일어나지 않을 시나리오를 처리합니다. 표준 JSON 인코더는 중복 필드를 생성하지 않습니다.
colm.anseo

20

도움이되는 문서에서 다음 예제를 찾았습니다 (source here ).

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

여기서 핵심은 OP가 디코딩을 찾고 있다는 것입니다.

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"(내부적으로 사용됨) 대신 "some-key"를 사용하여 JSON을 구문 분석합니다.


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)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.