Go에서 POST 요청으로 JSON 문자열을 보내는 방법


244

Apiary로 작업을 시도하고 JSON을 모의 서버로 보내고 다음 코드를 갖도록 범용 템플릿을 만들었습니다.

package main

import (
    "encoding/json"
    "fmt"
    "github.com/jmcvetta/napping"
    "log"
    "net/http"
)

func main() {
    url := "http://restapi3.apiary.io/notes"
    fmt.Println("URL:>", url)

    s := napping.Session{}
    h := &http.Header{}
    h.Set("X-Custom-Header", "myvalue")
    s.Header = h

    var jsonStr = []byte(`
{
    "title": "Buy cheese and bread for breakfast."
}`)

    var data map[string]json.RawMessage
    err := json.Unmarshal(jsonStr, &data)
    if err != nil {
        fmt.Println(err)
    }

    resp, err := s.Post(url, &data, nil, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("response Status:", resp.Status())
    fmt.Println("response Headers:", resp.HttpResponse().Header)
    fmt.Println("response Body:", resp.RawText())

}

이 코드는 JSON을 올바르게 보내지 않지만 이유를 모르겠습니다. JSON 문자열은 모든 호출에서 다를 수 있습니다. 나는 Struct이것을 사용할 수 없습니다 .


나는 당신이 사용하는 일부 라이브러리에 익숙하지 않지만, 그것을 이해하면서 Jsons의 맵을 보내려고합니다. 왜 json과 함께 문자열을 보내지 않습니까?
Topo

1
json을 보내려면 왜 json을 마샬링 해제합니까?
JimB 2016 년

작은 팁으로 메시지를 구조체 또는 map [string] 인터페이스 {}로 만들어 원하는 모든 값을 추가 한 다음 json.Marshall을 사용하여 맵 또는 구조체를 json으로 변환 할 수 있습니다.
Topo

@topo, 나는 낮잠의 소스 코드를 파헤 쳤습니다. 페이로드가 설정되면 호출합니다 json.Marshall. 왜 그에게 작동하지 않았는지 모르겠습니다.
OneOfOne 2016 년

답변:


502

나는 낮잠에 익숙하지 않지만 Golang의 net/http패키지를 사용하면 잘 작동합니다 ( playground ).

func main() {
    url := "http://restapi3.apiary.io/notes"
    fmt.Println("URL:>", url)

    var jsonStr = []byte(`{"title":"Buy cheese and bread for breakfast."}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Set("X-Custom-Header", "myvalue")
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("response Status:", resp.Status)
    fmt.Println("response Headers:", resp.Header)
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println("response Body:", string(body))
}

1
이제 놀이터에 패닉이 생겼습니다. 고치거나 무언가를 업데이트 할 수 있습니까?
Altenrion

9
@Altenrion 놀이터에서 작동하지 않습니다. 코드를 붙여 넣는 데 사용했습니다. 외부 연결을 열 수 없습니다.
OneOfOne

8
솔리드 밴드 이름 제안은 @Altenrion +1입니다.
Charlie Schliesser

8
단지 경고입니다. 기본적으로 golang http 클라이언트는 시간이 초과되지 않는다는 것을 잊지 마십시오. 실제 세계에서는client.Timeout = time.Second * 15
svarlamov

1
모든 오류를 수집 / 검사하도록 업데이트 할 수 있습니까? 이것은 적어도 Go의 게시물 요청을 한 Google의 결과이며 좋은 대답이지만 오류를 무시하는 많은 예제 코드가 표시되며 초보자에게는 나쁜 습관을 권장합니다. 다시 말하지만, 누군가가 정기적으로 오류를 무시한다면, 왜 결국에는하지 말아야하는지 배우는 것이 좋을 것입니다.
K. Rhoda

103

postjson을 게시 하는 데 사용할 수 있습니다 .

values := map[string]string{"username": username, "password": password}

jsonValue, _ := json.Marshal(values)

resp, err := http.Post(authAuthenticatorUrl, "application/json", bytes.NewBuffer(jsonValue))

3
이 오류가 발생합니다 :cannot use jsonValue (type []byte) as type io.Reader in argument to http.Post: []byte does not implement io.Reader (missing Read method)
Mandar Vaze

@MandarVaze 난 당신이 오해 할 수 있습니다 생각 io.Readerhttp.Post, 그리고 bytes.NewBuffer는 () 내 코드에서 잘 작동
gaozhidf

1
중요한 경우 1.7에 가고 있습니다. @OneOfOne에 의해 나열된 코드가 작동합니다 ( 대신 사용 bytes.NewBuffer()하지만 http.NewRequest대신 사용 http.Post)
Mandar Vaze

2
golang.org/pkg/net/http/#Post 에 따르면 , "발신자가 resp.Body읽은 io.Closer후에는 전화를 닫아야 합니다. 제공된 본문이 이면 요청 후 닫힙니다." Go 초보자 인 경우, 본문이 io.Closer, 즉이 예가 안전한 경우 어떻게 알 수 있습니까?
데니스

14

이미 구조체가 있다면.

type Student struct {
    Name    string `json:"name"`
    Address string `json:"address"`
}

// .....

body := &Student{
    Name:    "abc",
    Address: "xyz",
}

buf := new(bytes.Buffer)
json.NewEncoder(buf).Encode(body)
req, _ := http.NewRequest("POST", url, buf)

client := &http.Client{}
res, e := client.Do(req)
if e != nil {
    return e
}

defer res.Body.Close()

fmt.Println("response Status:", res.Status)
// Print the body to the stdout
io.Copy(os.Stdout, res.Body)

전체 요점 .


12

표준 net / http 패키지 외에도 net / http 를 감싸고 json 또는 구조체에 대해 너무 많이 생각하지 않고도 인생을 더 편하게 만드는 GoRequest 사용을 고려할 수 있습니다 . 그러나 한 번의 요청으로 두 가지를 혼합하고 일치시킬 수도 있습니다! (Gorequest github 페이지에서 자세한 내용을 볼 수 있습니다)

따라서 결국 코드는 다음과 같습니다.

func main() {
    url := "http://restapi3.apiary.io/notes"
    fmt.Println("URL:>", url)
    request := gorequest.New()
    titleList := []string{"title1", "title2", "title3"}
    for _, title := range titleList {
        resp, body, errs := request.Post(url).
            Set("X-Custom-Header", "myvalue").
            Send(`{"title":"` + title + `"}`).
            End()
        if errs != nil {
            fmt.Println(errs)
            os.Exit(1)
        }
        fmt.Println("response Status:", resp.Status)
        fmt.Println("response Headers:", resp.Header)
        fmt.Println("response Body:", body)
    }
}

이것은 달성하려는 방법에 따라 다릅니다. 나는 당신과 같은 문제가 있기 때문에이 라이브러리를 만들었고 코드가 짧고 json과 함께 사용하기 쉽고 코드베이스 및 프로덕션 시스템에서보다 유지 보수가 쉬운 코드를 원합니다.


GoRequest가 net / http를 래핑하는 경우 TLS에 대한 안전하지 않은 인증서를 비활성화하기 위해 이것을 추가 할 수 있습니까? tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }
user1513388

46
@ user1513388 어떤 언어로든 어떤 시나리오에서든 TLS 확인을 건너 뛰는 코드 예제를 제공하는 것은 항상 끔찍한 생각입니다. 우연히 StackOverflow를 방문하고 왜 고정 문제 의 본질을 이해하지 못하는 네오 피트의 많은 복사 / 붙여 넣기 "해결 방법" TLS 오류는 매우 중요합니다. 인증서 가져 오기 경로를 수정하거나 (테스트에 자체 서명을 사용하는 경우 가져 오기) 시스템의 인증서 체인을 수정하거나 서버가 클라이언트가 확인할 수없는 유효하지 않은 인증서를 제시하는 이유를 찾으십시오.
Mike Atlas

1
이 답변에 대해 내가 싫어하는 것 중 하나는 JSON 객체를 구성하는 방식인데, 이는 잠재적으로 주입을 통해 악용 될 수 있습니다. 더 좋은 방법은 객체를 작성한 다음 JSON으로 변환하는 것입니다 (적절한 이스케이프 사용).
John White

@JohnWhite 동의합니다, 매우 루비 / js / pythonic 느낌
Rambatino
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.