0

I am trying to use the http://ip-api.com/ api to get longitude and latitude through my ip address. When I access http://ip-api.com/json from my browser or with curl, it returns correct information in json. But when I try to use the API from my program, the API response has an empty body (or so it seems).

I'm trying to do it in this app. The Ip_response_success struct is made according to the api docs here http://ip-api.com/docs/api:json

type Ip_response_success struct {
    as          string
    city        string
    country     string
    countryCode string
    isp         string
    lat         string
    lon         string
    org         string
    query       string
    region      string
    regionName  string
    status      string  
    timezone    string
    zip         string
}

func Query(url string) (Ip_response_success, error) {
    resp, err := http.Get(url)
    if err != nil {
        return Ip_response_success{}, err
    }
    fmt.Printf("%#v\n", resp)

    var ip_response Ip_response_success
    defer resp.Body.Close()
    err = json.NewDecoder(resp.Body).Decode(&ip_response)
    if err != nil {
        return Ip_response_success{}, err
    }
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Printf("%#v\n", string(body))
    return ip_response, nil
}

func main() {
    ip, err := Query("http://ip-api.com/json")
    if err != nil {
        fmt.Printf("%#v\n", err)
    }
}

But the weirdest thing is the body of the response is blank. It gives a 200 status code in the response, so I assume there is no error on the API call. The API does not mention any auth requirements or user-agent requirements, and indeed it doesn't appear to need anything special when I curl it or access it through the browser. Is there any thing I'm doing wrong in my program or am I using the API wrong?

I try to print the response within the code, but resp.body just comes out as blank. Sample response from printing the http.Response struct :

&http.Response{Status:"200 OK", StatusCode:200, Proto:"HTTP/1.1", ProtoMajor:1, 
ProtoMinor:1, Header:http.Header{"Access-Control-Allow-Origin":[]string{"*"}, 
"Content-Type":[]string{"application/json; charset=utf-8"}, "Date":
[]string{"Tue, 21 Jun 2016 06:46:57 GMT"}, "Content-Length":[]string{"340"}}, 
Body:(*http.bodyEOFSignal)(0xc820010640), ContentLength:340, TransferEncoding:
[]string(nil), Close:false, Trailer:http.Header(nil), Request:(*http.Request)
(0xc8200c6000), TLS:(*tls.ConnectionState)(nil)}

Any help would be appreciated!

Julien Chien
  • 2,052
  • 4
  • 16
  • 32

1 Answers1

2

First of all, you must read the body and then parse it:

body, err := ioutil.ReadAll(resp.Body)
err = json.NewDecoder(body).Decode(&ip_response)
if err != nil {
    return Ip_response_success{}, err
}

Plus, in go, the json decoder must be able to access to the fields of a struct. That means they must be exposed outside of your package.

That implies you use the json annotation to specify the mapping:

type Ip_response_success struct {
    As          string `json: "as"`
    City        string `json: "city"`
    Country     string `json: "country"`
    CountryCode string `json: "countryCode"`
    Isp         string `json: "isp"`
    Lat         float64 `json: "lat"`
    Lon         float64 `json: "lon"`
    Org         string `json: "org"`
    Query       string `json: "query"`
    Region      string `json: "region"`
    RegionName  string `json: "regionName"`
    Status      string `json: "status"`
    Timezone    string `json: "timezone"`
    Zip         string `json: "zip"`
}

Note also that I changed the Lon / Lat type to float64 according to the data sent by the server

poussma
  • 7,033
  • 3
  • 43
  • 68