Reading content from http.Get in Golang - http

I'm having a tough time reading XML from a GET request in Go. I just started to learn Go and haven't found any resources on this topic. What I tried:
response, err := http.Get(url)
if err != nil {
log.Fatal(err)
} else {
defer response.Body.Close()
xml, _ := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
}
_, err := io.Copy(os.Stdout, response.Body) works but I'd like to store the XML for further processing.
Any help is greatly appreciated.

What you've tried is mostly good. Few things to improve it:
http.Get() returns an http.Response and an optional error. If there is no error, that only means that the HTTP GET operation succeeded, but the server might have responded with an error document. So you still have to check the response HTTP status code.
Also io.ReadAll() also returns an error (besides the read data), don't forget to check that too.
Let's wrap it in a function:
func getXML(url string) (string, error) {
resp, err := http.Get(url)
if err != nil {
return "", fmt.Errorf("GET error: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Status error: %v", resp.StatusCode)
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Read body: %v", err)
}
return string(data), nil
}
Testing / using the above function:
if xmlStr, err := getXML("http://somehost.com/some.xml"); err != nil {
log.Printf("Failed to get XML: %v", err)
} else {
log.Println("Received XML:")
log.Println(xmlStr)
}
Also note that it would be the same to get the content of any other responses, so it's worth not "encoding" the string conversion and return type. This one is more general:
func getContent(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("GET error: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Status error: %v", resp.StatusCode)
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Read body: %v", err)
}
return data, nil
}
Using this to get an XML doc:
if data, err := getContent("http://somehost.com/some.xml"); err != nil {
log.Printf("Failed to get XML: %v", err)
} else {
log.Println("Received XML:")
log.Println(string(data))
}

Related

Golang large response unexpected end of JSON input

I'm sending a request to a server and trying to unmarshall HTTP Response. It's weird, but some of the responses return 200 and do not give an unmarshall error, while some of them return 200 and give an unmarshall error.
My client code looks like:
func SendRequest(requestModel *model.Request) (*model.Response, error) {
responseModel := &dspModel.Response{}
byteData, err := json.Marshal(requestModel)
if err != nil {
zap.S().Errorf("Error marshalling request. Err: %v", err)
return nil, err
}
url := "xx"
request, _ := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(byteData))
request.Close = true
client := &http.Client{}
response, err := client.Do(request)
if err, ok := err.(net.Error); ok && err.Timeout() {
zap.S().Error("Response timeout exceed")
return nil, errors.New("Response timeout exceed")
}
if err != nil || response == nil {
errorMessage := "not respond"
zap.S().Error(errorMessage)
return nil, errors.New(errorMessage)
}
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
err = json.NewDecoder(response.Body).Decode(&responseModel)
if err != nil {
// Error occurred here!
errorMessage := "Request response decode error"
zap.S().Errorf("%v, Err: %v", errorMessage, err)
return nil, errors.New(errorMessage)
}
return response, nil
} else if response.StatusCode == http.StatusNoContent {
return nil, nil
} else {
bodyBytes, _ := ioutil.ReadAll(response.Body)
errorMessage := "not respond"
zap.S().Errorf("%v, StatusCode %v, Response: %v Request: %v", errorMessage,
response.StatusCode, string(bodyBytes), string(byteData))
return nil, errors.New(errorMessage)
}
}
I suspect response cause it's too long and has different characters.
Response looks like:
{"tax":{"ver":"1.0"},"cur":"EUR","rack":[{"tur":[{"zar":2.599886212,"domain":["test.com"],"ney":"https://censored.com/nimp?fuid=&ic=EWRMh-.UTPdNvWB-JYa58c85N0fEPgXunKp3wwyPadp7jwqHgZbz4dG0A51OVO-2Gs0znYmLcPIH0ThEmpsYl8wKofo9ytJ2A3uWr9Kn-dNxeh.k8lIml9kavPk1.dk7f.46xKX7IVpf3.yU-Yx1KetQl3Q9f-iePn7B86yjVgMxkTNfhZAg0pP0kKZaJMd2orLXoV4xPXmwTdfJbWJU5bGAUROJT-Yd7yTHoVveuvOBClHzM4cgHFmGxzox6cCJ2gZB.7fqKkPzECXwdpobmO0RWxdu224-FADd.oM4DghIEpdZJe.FjEq0stQnJBT.puw0JamHgT15NdSQN7voBJ8UqGCDOu1qSLece6Iy.PN392xGWhxs0URbrWhSEgkhCr.R4ol9kjrMqK78shw2gHBJjEzKeBeo6lBzU8YfoKDM7oPlj5SwmL6sV2i2UaWJbEtreRt3oABDPab--AevfJW2rQ0-2iyt-rJSPjDHHoOQEFoh0G7cPm8SIZxk17ojWkFdM7CXlmuSN0paqMhp-4gWlfgvNq8a65I8GfY8cwVrW5KzRszHLhWYareVM3MNpejdcVH2kinEnYzBVyW0e8oN06LC2icG8FRlhOC2N8wni66liT73RvKyFFT1zW7SAoqtgn9KXY6m.EaZzSx3aapIMGpG9-S8q6mwAuwZId37ri4GTiLXp6OMABsLwT3sMUOm.Kktp.uYP1z2be2DFM6zKKPL7YJopAvdfS7TdhMfHD6Dfcv-EvK6Q0lNylaFIjegbNjPEPPXzLPdf8iwLK-dqfSe127TZcj5xJUFwo45IgFl0i0puKRIzsAtM2W3zM-TNc2HEc0nIllK.aoKZ0tF9iSekzjcNnMAvhcAKBqq6DY.qIBUs5yOoxqW4m-ga9drHp09VXIkn7st7J4IUlrMZFuVHnnzbeqD61AKKFiaRCqPee6Y88DqhsdNt7SzdA-xq9SKnJW67zsZTD0T9OoRl3.nLaSwoQ==&t=adi&prc=${censored}","tax":{"name":"test"},"hy":3020,"ny":2530,"arz":"<script type=\"text/javascript\">var _CDurl='';(function(){_CDurl=encodeURIComponent(document.URL);})();document.write('<scr' + 'ipt type=\"text/javascript\" src=\"https://censored/at?bad=&gad=&ic=EWRMh-.UTPdNvWB-JYa58c85N0fEPgXunKp3wwyPadp7jwqHgZbz4dG0A51OVO-2Gs0znYmLcPIH0ThEmpsYl8wKofo9ytJ2A3uWr9Kn-dNxeh.k8lIml9kavPk1.dk7f.46xKX7IVpf3.yU-Yx1KetQl3Q9f-iePn7B86yjVgMxkTNfhZAg0pP0kKZaJMd2orLXoV4xPXmwTdfJbWJU5bGAUROJT-Yd7yTHoVveuvOBClHzM4cgHFmGxzox6cCJ2gZB.7fqKkPzECXwdpobmO0RWxdu224-FADd.oM4DghIEpdZJe.FjEq0stQnJBT.puw0JamHgT15NdSQN7voBJ8UqGCDOu1qSLece6Iy.PN392xGWhxs0URbrWhSEgkhCr.R4ol9kjrMqK78shw2gHBJjEzKeBeo6lBzU8YfoKDM7oPlj5SwmL6sV2i2UaWJbEtreRt3oABDPab--AevfJW2rQ0-2iyt-rJSPjDHHoOQEFoh0G7cPm8SIZxk17ojWkFdM7CXlmuSN0paqMhp-4gWlfgvNq8a65I8GfY8cwVrW5KzRszHLhWYareVM3MNpejdcVH2kinEnYzBVyW0e8oN06LC2icG8FRlhOC2N8wni66liT73RvKyFFT1zW7SAoqtgn9KXY6m.EaZzSx3aapIMGpG9-S8q6mwAuwZId37ri4GTiLXp6OMABsLwT3sMUOm.Kktp.uYP1z2be2DFM6zKKPL7YJopAvdfS7TdhMfHD6Dfcv-EvK6Q0lNylaFIjegbNjPEPPXzLPdf8iwLK-dqfSe127TZcj5xJUFwo45IgFl0i0puKRIzsAtM2W3zM-TNc2HEc0nIllK.aoKZ0tF9iSekzjcNnMAvhcAKBqq6DY.qIBUs5yOoxqW4m-ga9drHp09VXIkn7st7J4IUlrMZFuVHnnzbeqD61AKKFiaRCqPee6Y88DqhsdNt7SzdA-xq9SKnJW67zsZTD0T9OoRl3.nLaSwoQ==&t=adj&prc=${censored}&tat='+_CDurl+'\"></scr' + 'ipt>');</script>"}],"tark":"1"}],"gno":"55f03d71-f021-49e0-a1a5-cae4315b3561"}
When I debug the error, I noticed that after half of the response is not visible.
Error statement:
"msg":"Request response decode error, Response: {\"tax\":{\"ver\":\"1.0\"},\"cur\":\"EUR\",\"rac\":[{\"btyrd\":[{\"zar\":2.599886212,\"domain\":[\"test.com\"],\"ney\":\"https://censored/nimp?fuid=&ic=EWRMh-.UTPdNvWB-JYa58c85N0fEPgXunKp3wwyPadp7jwqHgZbz4dG0A51OVO-2Gs0znYmLcPIH0ThEmpsYl8wKofo9ytJ2A3uWr9Kn-dNxeh.k8lIml9kavPk1.dk7f.46xKX7IVpf3.yU-Yx1KetQl3Q9f-iePn7B86yjVgMxkTNfhZAg0pP0kKZaJMd2orLXoV4xPXmwTdfJbWJU5bGAUROJT-Yd7yTHoVveuvOBClHzM4cgHFmGxzox6cCJ2gZB.7fqKkPzECXwdpobmO0RWxdu224-FADd.oM4DghIEpdZJe.FjEq0stQnJBT.puw0JamHgT15NdSQN7voBJ8UqGCDOu1q, Err: unexpected end of JSON input"
Response Model:
type Response struct {
Tax Tax `json:"tax"`
Cur string `json:"cur"`
Rack []Rack `json:"rack"`
Gno string `json:"gno"`
}
type Tax struct {
Ver string `json:"ver"`
}
type TaxOfTur struct {
Name string `json:"name"`
}
type Tur struct {
Zar float64 `json:"zar"`
Domain []string `json:"domain"`
Ney string `json:"ney"`
Tax TaxOfTur `json:"tax"`
Hy int `json:"hy"`
Ny int `json:"ny"`
Arz string `json:"arz"`
}
type Rack struct {
Tur []Tur `json:"tur"`
Tark string `json:"tark"`
}
Probably unrelated but requests are going concurrent and my test case is based on only one concurrent request. So how can I solve this problem?
I tried to implement your code at my end, and it's giving the expected output. The code I tried is given below.
Note: This is not a solution to the exact problem, since I was not able to reproduce the same.
// Response struct definition here...
// ..
// The trouble making JSON.
const msg = `{"tax":{"ver":"1.0"},"cur":"EUR","rack":[{"tur":[{"zar":2.599886212,"domain":["test.com"],"ney":"https://censored.com/nimp?fuid=&ic=EWRMh-.UTPdNvWB-JYa58c85N0fEPgXunKp3wwyPadp7jwqHgZbz4dG0A51OVO-2Gs0znYmLcPIH0ThEmpsYl8wKofo9ytJ2A3uWr9Kn-dNxeh.k8lIml9kavPk1.dk7f.46xKX7IVpf3.yU-Yx1KetQl3Q9f-iePn7B86yjVgMxkTNfhZAg0pP0kKZaJMd2orLXoV4xPXmwTdfJbWJU5bGAUROJT-Yd7yTHoVveuvOBClHzM4cgHFmGxzox6cCJ2gZB.7fqKkPzECXwdpobmO0RWxdu224-FADd.oM4DghIEpdZJe.FjEq0stQnJBT.puw0JamHgT15NdSQN7voBJ8UqGCDOu1qSLece6Iy.PN392xGWhxs0URbrWhSEgkhCr.R4ol9kjrMqK78shw2gHBJjEzKeBeo6lBzU8YfoKDM7oPlj5SwmL6sV2i2UaWJbEtreRt3oABDPab--AevfJW2rQ0-2iyt-rJSPjDHHoOQEFoh0G7cPm8SIZxk17ojWkFdM7CXlmuSN0paqMhp-4gWlfgvNq8a65I8GfY8cwVrW5KzRszHLhWYareVM3MNpejdcVH2kinEnYzBVyW0e8oN06LC2icG8FRlhOC2N8wni66liT73RvKyFFT1zW7SAoqtgn9KXY6m.EaZzSx3aapIMGpG9-S8q6mwAuwZId37ri4GTiLXp6OMABsLwT3sMUOm.Kktp.uYP1z2be2DFM6zKKPL7YJopAvdfS7TdhMfHD6Dfcv-EvK6Q0lNylaFIjegbNjPEPPXzLPdf8iwLK-dqfSe127TZcj5xJUFwo45IgFl0i0puKRIzsAtM2W3zM-TNc2HEc0nIllK.aoKZ0tF9iSekzjcNnMAvhcAKBqq6DY.qIBUs5yOoxqW4m-ga9drHp09VXIkn7st7J4IUlrMZFuVHnnzbeqD61AKKFiaRCqPee6Y88DqhsdNt7SzdA-xq9SKnJW67zsZTD0T9OoRl3.nLaSwoQ==&t=adi&prc=${censored}","tax":{"name":"test"},"hy":3020,"ny":2530,"arz":"<script type=\"text/javascript\">var _CDurl='';(function(){_CDurl=encodeURIComponent(document.URL);})();document.write('<scr' + 'ipt type=\"text/javascript\" src=\"https://censored/at?bad=&gad=&ic=EWRMh-.UTPdNvWB-JYa58c85N0fEPgXunKp3wwyPadp7jwqHgZbz4dG0A51OVO-2Gs0znYmLcPIH0ThEmpsYl8wKofo9ytJ2A3uWr9Kn-dNxeh.k8lIml9kavPk1.dk7f.46xKX7IVpf3.yU-Yx1KetQl3Q9f-iePn7B86yjVgMxkTNfhZAg0pP0kKZaJMd2orLXoV4xPXmwTdfJbWJU5bGAUROJT-Yd7yTHoVveuvOBClHzM4cgHFmGxzox6cCJ2gZB.7fqKkPzECXwdpobmO0RWxdu224-FADd.oM4DghIEpdZJe.FjEq0stQnJBT.puw0JamHgT15NdSQN7voBJ8UqGCDOu1qSLece6Iy.PN392xGWhxs0URbrWhSEgkhCr.R4ol9kjrMqK78shw2gHBJjEzKeBeo6lBzU8YfoKDM7oPlj5SwmL6sV2i2UaWJbEtreRt3oABDPab--AevfJW2rQ0-2iyt-rJSPjDHHoOQEFoh0G7cPm8SIZxk17ojWkFdM7CXlmuSN0paqMhp-4gWlfgvNq8a65I8GfY8cwVrW5KzRszHLhWYareVM3MNpejdcVH2kinEnYzBVyW0e8oN06LC2icG8FRlhOC2N8wni66liT73RvKyFFT1zW7SAoqtgn9KXY6m.EaZzSx3aapIMGpG9-S8q6mwAuwZId37ri4GTiLXp6OMABsLwT3sMUOm.Kktp.uYP1z2be2DFM6zKKPL7YJopAvdfS7TdhMfHD6Dfcv-EvK6Q0lNylaFIjegbNjPEPPXzLPdf8iwLK-dqfSe127TZcj5xJUFwo45IgFl0i0puKRIzsAtM2W3zM-TNc2HEc0nIllK.aoKZ0tF9iSekzjcNnMAvhcAKBqq6DY.qIBUs5yOoxqW4m-ga9drHp09VXIkn7st7J4IUlrMZFuVHnnzbeqD61AKKFiaRCqPee6Y88DqhsdNt7SzdA-xq9SKnJW67zsZTD0T9OoRl3.nLaSwoQ==&t=adj&prc=${censored}&tat='+_CDurl+'\"></scr' + 'ipt>');</script>"}],"tark":"1"}],"gno":"55f03d71-f021-49e0-a1a5-cae4315b3561"}`
func SendRequest() (*Response, error) {
url := "http://localhost:8080/foo" // dummy server
request, _ := http.NewRequest(http.MethodPost, url, nil)
request.Close = true
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
resBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
responseModel := &Response{}
err = json.Unmarshal(resBody, &responseModel)
if err != nil {
return nil, err
}
return responseModel, nil
}
func StartDummyServer() {
handler := func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, msg)
}
http.HandleFunc("/foo", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func main() {
// a dummy server is created to send the response
go StartDummyServer()
time.Sleep(time.Second)
resp, err := SendRequest()
if err != nil {
log.Fatal(err.Error())
}
fmt.Println(prettyPrint(resp))
time.Sleep(time.Second * 10)
}
func prettyPrint(i interface{}) string {
s, _ := json.MarshalIndent(i, "", " ")
return string(s)
}
Here is the console output:
{
"tax": {
"ver": "1.0"
},
"cur": "EUR",
"rack": [
{
"tur": [
{
"zar": 2.599886212,
"domain": [
"test.com"
],
"ney": "https://censored.com/nimp?fuid=\u0026ic=EWRMh-.UTPdNvWB-JYa58c85N0fEPgXunKp3wwyPadp7jwqHgZbz4dG0A51OVO-2Gs0znYmLcPIH0ThEmpsYl8wKofo9ytJ2A3uWr9Kn-dNxeh.k8lIml9kavPk1.dk7f.46xKX7IVpf3.yU-Yx1KetQl3Q9f-iePn7B86yjVgMxkTNfhZAg0pP0kKZaJMd2orLXoV4xPXmwTdfJbWJU5bGAUROJT-Yd7yTHoVveuvOBClHzM4cgHFmGxzox6cCJ2gZB.7fqKkPzECXwdpobmO0RWxdu224-FADd.oM4DghIEpdZJe.FjEq0stQnJBT.puw0JamHgT15NdSQN7voBJ8UqGCDOu1qSLece6Iy.PN392xGWhxs0URbrWhSEgkhCr.R4ol9kjrMqK78shw2gHBJjEzKeBeo6lBzU8YfoKDM7oPlj5SwmL6sV2i2UaWJbEtreRt3oABDPab--AevfJW2rQ0-2iyt-rJSPjDHHoOQEFoh0G7cPm8SIZxk17ojWkFdM7CXlmuSN0paqMhp-4gWlfgvNq8a65I8GfY8cwVrW5KzRszHLhWYareVM3MNpejdcVH2kinEnYzBVyW0e8oN06LC2icG8FRlhOC2N8wni66liT73RvKyFFT1zW7SAoqtgn9KXY6m.EaZzSx3aapIMGpG9-S8q6mwAuwZId37ri4GTiLXp6OMABsLwT3sMUOm.Kktp.uYP1z2be2DFM6zKKPL7YJopAvdfS7TdhMfHD6Dfcv-EvK6Q0lNylaFIjegbNjPEPPXzLPdf8iwLK-dqfSe127TZcj5xJUFwo45IgFl0i0puKRIzsAtM2W3zM-TNc2HEc0nIllK.aoKZ0tF9iSekzjcNnMAvhcAKBqq6DY.qIBUs5yOoxqW4m-ga9drHp09VXIkn7st7J4IUlrMZFuVHnnzbeqD61AKKFiaRCqPee6Y88DqhsdNt7SzdA-xq9SKnJW67zsZTD0T9OoRl3.nLaSwoQ==\u0026t=adi\u0026prc=${censored}",
"tax": {
"name": "test"
},
"hy": 3020,
"ny": 2530,
"arz": "\u003cscript type=\"text/javascript\"\u003evar _CDurl='';(function(){_CDurl=encodeURIComponent(document.URL);})();document.write('\u003cscr' + 'ipt type=\"text/javascript\" src=\"https://censored/at?bad=\u0026gad=\u0026ic=EWRMh-.UTPdNvWB-JYa58c85N0fEPgXunKp3wwyPadp7jwqHgZbz4dG0A51OVO-2Gs0znYmLcPIH0ThEmpsYl8wKofo9ytJ2A3uWr9Kn-dNxeh.k8lIml9kavPk1.dk7f.46xKX7IVpf3.yU-Yx1KetQl3Q9f-iePn7B86yjVgMxkTNfhZAg0pP0kKZaJMd2orLXoV4xPXmwTdfJbWJU5bGAUROJT-Yd7yTHoVveuvOBClHzM4cgHFmGxzox6cCJ2gZB.7fqKkPzECXwdpobmO0RWxdu224-FADd.oM4DghIEpdZJe.FjEq0stQnJBT.puw0JamHgT15NdSQN7voBJ8UqGCDOu1qSLece6Iy.PN392xGWhxs0URbrWhSEgkhCr.R4ol9kjrMqK78shw2gHBJjEzKeBeo6lBzU8YfoKDM7oPlj5SwmL6sV2i2UaWJbEtreRt3oABDPab--AevfJW2rQ0-2iyt-rJSPjDHHoOQEFoh0G7cPm8SIZxk17ojWkFdM7CXlmuSN0paqMhp-4gWlfgvNq8a65I8GfY8cwVrW5KzRszHLhWYareVM3MNpejdcVH2kinEnYzBVyW0e8oN06LC2icG8FRlhOC2N8wni66liT73RvKyFFT1zW7SAoqtgn9KXY6m.EaZzSx3aapIMGpG9-S8q6mwAuwZId37ri4GTiLXp6OMABsLwT3sMUOm.Kktp.uYP1z2be2DFM6zKKPL7YJopAvdfS7TdhMfHD6Dfcv-EvK6Q0lNylaFIjegbNjPEPPXzLPdf8iwLK-dqfSe127TZcj5xJUFwo45IgFl0i0puKRIzsAtM2W3zM-TNc2HEc0nIllK.aoKZ0tF9iSekzjcNnMAvhcAKBqq6DY.qIBUs5yOoxqW4m-ga9drHp09VXIkn7st7J4IUlrMZFuVHnnzbeqD61AKKFiaRCqPee6Y88DqhsdNt7SzdA-xq9SKnJW67zsZTD0T9OoRl3.nLaSwoQ==\u0026t=adj\u0026prc=${censored}\u0026tat='+_CDurl+'\"\u003e\u003c/scr' + 'ipt\u003e');\u003c/script\u003e"
}
],
"tark": "1"
}
],
"gno": "55f03d71-f021-49e0-a1a5-cae4315b3561"
}
Also, I used the same code you are using, after commenting off the request body part(since I don't have it) and changing the function signature(of SendRequest) a bit as given below. All others are as same as the code given above.
func SendRequest() (*Response, error) {
responseModel := &Response{}
// Commented off since the response body is not with me..
// byteData, err := json.Marshal(requestModel)
// if err != nil {
// zap.S().Errorf("Error marshalling request. Err: %v", err)
// return nil, err
// }
url := "http://localhost:8080/foo"
request, _ := http.NewRequest(http.MethodPost, url /*bytes.NewBuffer(byteData)*/, nil)
request.Close = true
client := &http.Client{}
response, err := client.Do(request)
if err, ok := err.(net.Error); ok && err.Timeout() {
log.Fatal("Response timeout exceed")
return nil, errors.New("Response timeout exceed")
}
if err != nil || response == nil {
errorMessage := "not respond"
log.Fatal(errorMessage)
return nil, errors.New(errorMessage)
}
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
err = json.NewDecoder(response.Body).Decode(&responseModel)
if err != nil {
errorMessage := "Request response decode error"
log.Printf("%v, Err: %v", errorMessage, err)
return nil, errors.New(errorMessage)
}
return responseModel, nil
} else {
bodyBytes, _ := ioutil.ReadAll(response.Body)
errorMessage := "not respond"
log.Printf("%v, StatusCode %v, Response: %v\n", errorMessage, response.StatusCode, string(bodyBytes))
return nil, errors.New(errorMessage)
}
}
With this as well, I am able to Unmarshal the response successfully.
So, in my opinion, the incoming response is not correct(may be). Try printing the incoming data and verify.
resBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
fmt.Printf("%s", resBody) // 👈

multipart writer CreateFormFile stuck

trying to post multipart/form-data image using go
image file receive from request client and already saved as multipart.File
here my code
func postImage(file multipart.File, url string, filename string) (*http.Response, error) {
r, w := io.Pipe()
defer w.Close()
m := multipart.NewWriter(w)
defer m.Close()
errchan := make(chan error)
defer close(errchan)
go func() {
part, err := m.CreateFormFile("file", filename)
log.Println(err)
if err != nil {
errchan <- err
return
}
if _, err := io.Copy(part, file); err != nil {
errchan <- err
return
}
}()
merr := <-errchan
if merr != nil {
return nil, merr
}
resp, err := http.Post(url, m.FormDataContentType(), r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return resp, err
}
when i try using it, it stuck at part, err := m.CreateFormFile("file", filename) never return anything
any solution?
Thanks
Use the pipe error to propagate the error back to the main goroutine. Close the write side of the pipe to prevent the client from blocking forever on read. Close the read side of the pipe to ensure that the goroutine exits.
func postImage(file multipart.File, url string, filename string) (*http.Response, error) {
r, w := io.Pipe()
// Close the read side of the pipe to ensure that
// the goroutine exits in the case where http.Post
// does not read all of the request body.
defer r.Close()
m := multipart.NewWriter(w)
go func() {
part, err := m.CreateFormFile("file", filename)
if err != nil {
// The error is returned from read on the pipe.
w.CloseWithError(err)
return
}
if _, err := io.Copy(part, file); err != nil {
// The error is returned from read on the pipe.
w.CloseWithError(err)
return
}
// The http.Post function reads the pipe until
// an error or EOF. Close to return an EOF to
// http.Post.
w.Close()
}()
resp, err := http.Post(url, m.FormDataContentType(), r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return resp, err
}

request.FormValue is empty in the 'net/http' in Go

I'm trying to create a simple http service with the endpoint to download file to the local system in Go. The link comes in ?uri tag, but when I want to get it I receive an empty string. I tried to parse the form of my request but it didn't help. Here is my code:
func main() {
http.HandleFunc("/download", DownloadHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func DownloadHandler(writer http.ResponseWriter, request *http.Request) {
prsErr := request.ParseForm()
if prsErr != nil{
panic(prsErr)
}
uri := request.FormValue("?uri")
_, _ = writer.Write([]byte(uri))
err := DownloadFile("img.png", uri)
if err != nil {
panic(err)
}
}
func DownloadFile(filepath string, url string) error {
// Create the file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Write the body to file
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}
I will appreciate any help! Thank you!
invalid at request.FormValue("?uri")
uri := request.FormValue("uri")

Stream HAR events

I have a long running app that I'd like to monitor in real time. HAR files allow me to do this after the fact, but as they are an "archive", they don't allow me to do this in real time.
Is their anyway to stream the "events" array of the HAR file so I can process them as they are generated?
This can be firefox or chrome.
So with some help from https://github.com/mafredri/cdp/tree/master/example/screencast I figured out how to do this in go with chrome's debugger api
What this code doesn't do is tie the request body to the response (where it isn't available), but as I show the RequestID will be consistent so if one serializes event processing (say via locking) one can save the body and use it when the response event is seen.
package main
import (
"context"
"log"
"github.com/mafredri/cdp"
"github.com/mafredri/cdp/cdpcmd"
"github.com/mafredri/cdp/devtool"
"github.com/mafredri/cdp/rpcc"
)
func main() {
if err := run(); err != nil {
panic(err)
}
}
func run() error {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
devt := devtool.New("http://localhost:9222")
page, err := devt.Get(ctx, devtool.Page)
if err != nil {
return err
}
conn, err := rpcc.DialContext(ctx, page.WebSocketDebuggerURL)
if err != nil {
return err
}
defer conn.Close()
c := cdp.NewClient(conn)
err = c.Page.Enable(ctx)
if err != nil {
return err
}
loadEventFired, err := c.Page.LoadEventFired(ctx)
if err != nil {
return err
}
_, err = c.Page.Navigate(ctx, cdpcmd.NewPageNavigateArgs("https://github.com/"))
if err != nil {
return err
}
_, err = loadEventFired.Recv()
if err != nil {
return err
}
loadEventFired.Close()
a := &cdpcmd.NetworkEnableArgs{}
a.SetMaxResourceBufferSize(32000)
a.SetMaxTotalBufferSize(96000)
err = c.Network.Enable(ctx, a)
responseEvents, err := c.Network.ResponseReceived(ctx)
requestEvents, err := c.Network.RequestWillBeSent(ctx)
go func() {
defer responseEvents.Close()
for {
ev, err := responseEvents.Recv()
if err != nil {
log.Printf("Failed to receive network event: %v", err)
return
}
log.Printf("requestid = %v, url = %v", ev.RequestID, ev.Response.URL)
}
}()
go func() {
defer requestEvents.Close()
for {
ev, err := requestEvents.Recv()
if err != nil {
log.Printf("Failed to receive network event: %v", err)
return
}
log.Printf("requestid = %v, url = %v", ev.RequestID, ev.Request.URL)
}
}()
select {}
return nil
}

Can I post with Content-Type: multipart/form-data

How do I POST to an API with Content-Type: multipart/form-data, []byte parameters and string arguments? I have tried, but it is failing.
Error message:
details: "[301 301 Moved Permanently]<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<html>\r\n301 Moved Permanently\r\n<body bgcolor=\"white\">\r\n301 Moved Permanently\r\n<p>The requested resource has been assigned a new permanent URI.</p >\r\n<hr/>Powered by Tengine/2.1.0</body>\r\n</html>\r\n"
Go code:
func NewPost2(url string) ([]byte, error) {
m := make(map[string]interface{}, 0)
m["fileName"] ="good"
m["name"] = Base64ToByte("/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDHooor+wD+Zz//2Q==")
b, _ := json.Marshal(m)
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
httpReq.Header.Set("Content-Type", "multipart/form-data;charset=UTF-8")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
b, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
}
respData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return respData, nil
}
Now, I am very happy with the mood to share my solution
func NewPostFile(url string, paramTexts map[string]interface{}, paramFile FileItem) ([]byte, error) {
// if paramFiles ==nil {
// return NewPost(url,paramTexts,header,transport)
// }
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for k, v := range paramTexts {
bodyWriter.WriteField(k, v.(string))
}
fileWriter, err := bodyWriter.CreateFormFile(paramFile.Key, paramFile.FileName)
if err != nil {
fmt.Println(err)
//fmt.Println("Create form file error: ", error)
return nil, err
}
fileWriter.Write(paramFile.Content)
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
fmt.Println(bodyBuf.String())
resp, err := http.Post(url, contentType, bodyBuf)
if err != nil {
return nil, err
}
defer resp.Body.Close()
fmt.Println(resp)
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
b, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
}
respData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
fmt.Println(string(respData))
return respData, nil
}
type FileItem struct {
Key string //image_content
FileName string //test.jpg
Content []byte //[]byte
}
I wrapped the multipart code in a function, as you need to Close it before you
can make a request. Also my method is using # as a heuristic, similar to
cURL [1]:
package main
import (
"bytes"
"io"
"mime/multipart"
"os"
"strings"
)
func createForm(form map[string]string) (string, io.Reader, error) {
body := new(bytes.Buffer)
mp := multipart.NewWriter(body)
defer mp.Close()
for key, val := range form {
if strings.HasPrefix(val, "#") {
val = val[1:]
file, err := os.Open(val)
if err != nil { return "", nil, err }
defer file.Close()
part, err := mp.CreateFormFile(key, val)
if err != nil { return "", nil, err }
io.Copy(part, file)
} else {
mp.WriteField(key, val)
}
}
return mp.FormDataContentType(), body, nil
}
Example:
package main
import "net/http"
func main() {
form := map[string]string{"profile": "#portrait.jpg", "name": "John"}
ct, body, err := createForm(form)
if err != nil {
panic(err)
}
http.Post("https://stackoverflow.com", ct, body)
}
https://curl.se/docs/manpage.html#-F
On a 301 response, the new url is specified in the headers of the response, not its body
(see https://en.wikipedia.org/wiki/HTTP_301)
try printing :
resp.Header["Location"]
If you have this error as a final response, this also means that the http.Client chose to not follow this redirection.
The doc says that the dafult policy for a Client is to follow up to 10 redirects.
In order to debug redirects, you can write your own CheckRedirect function, which can for instance print the sequence of urls

Resources