Question

I am trying to implement the following PHP code in Google App Engine Go:

<?php

function api_query(array $req = array()) {
        $key = '90294318da0162b082c3d27126be80c3873955f9';

        $req['method'] = 'getinfo';
        $req['nonce'] = 1394503747386411;

        // generate the POST data string
        $post_data = http_build_query($req, '', '&');
        $sign = '75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348';

        // generate the extra headers
        $headers = array(
                'Sign: '.$sign,
                'Key: '.$key,
        );

        // our curl handle (initialize if required)
        static $ch = null;
        if (is_null($ch)) {
                $ch = curl_init();
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; Cryptsy API PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
        }
        curl_setopt($ch, CURLOPT_URL, 'https://api.cryptsy.com/api');
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

        // run the query
        $res = curl_exec($ch);

        if ($res === false) throw new Exception('Could not get reply: '.curl_error($ch));
        $dec = json_decode($res, true);
        if (!$dec) throw new Exception('Invalid data received, please make sure connection is working and requested API exists');

        echo "<pre>".print_r($dec, true)."</pre>";
        return $dec;
}

api_query();

When executed, the code returns a JSON array of values. I tried implementing the same code in Golang:

func PrivateCall(c appengine.Context) (map[string]interface{}, error) {
    AuthAPI := "https://api.cryptsy.com/api"
    APIKey := "90294318da0162b082c3d27126be80c3873955f9"
    tr := urlfetch.Transport{Context: c}
    values := url.Values{}
    values.Set("method", "getinfo")
    values.Set("nonce", "1394503747386411")

    signature := "75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348"

    req, err := http.NewRequest("POST", AuthAPI+"?"+values.Encode(), nil)
    if err != nil {
        c.Infof("API - Call - error 2 - %s", err.Error())
        return nil, err
    }
    req.Header.Set("Key", APIKey)
    req.Header.Set("Sign", signature)

    c.Infof("req - %v", req)
    resp, err := tr.RoundTrip(req)
    if err != nil {
        c.Errorf("API post error: %s", err)
        return nil, err
    }
    defer resp.Body.Close()
    //reading response
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        c.Errorf("API read error: could not read body: %s", err)
        return nil, err
    }
    result := make(map[string]interface{})
    //unmarshalling JSON response
    err = json.Unmarshal(body, &result)
    if err != nil {
        c.Infof("Unmarshal: %v", err)
        c.Infof("%s", body)
        return nil, err
    }
    return result, nil
}

I am getting an error saying "Unable to Authorize Request - Check Your Post Data". Does anyone see what could've caused this error? At the moment my best guess is that perhaps the request header in Go is a map[string][]string, while in PHP it appears to be an array...

Était-ce utile?

La solution

As suggested by LeGEC, you're putting the POST data onto the end of the URL as if it were a GET request.

Try replacing

values := url.Values{}
values.Set("method", "getinfo")
values.Set("nonce", "1394503747386411")

signature := "75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348"

req, err := http.NewRequest("POST", AuthAPI+"?"+values.Encode(), nil)

with

data := struct {
    method string
    nonce  string
}{
    "getinfo",
    "1394503747386411",
}
signature := "75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348"
postData, err := json.Marshal(data)
if err != nil {
    return nil, err
}
buf := bytes.NewBuffer(postData)
req, err := http.Post(AuthAPI, "application/json", buf)

Autres conseils

https://github.com/ctfang/http_build_query

data := map[string]interface{}{
    "int":     1,
    "str":     "str",
    "arr_int": []int16{1, -2, 4},
    "m_arr": map[string][]int16{
        "test": []int16{1, -2, 4},
    },
    "m_m": []interface{}{
        map[string]string{"mo1": "v", "mo2": "v2"},
        map[string]string{"mo2": "v"},
    },
    "m_m_m": map[string]interface{}{
        "mm": struct{ Name string }{"张三"},
    },
}
str := Encode(data)

// echo 
// int=1&str=str&arr_int[]=1&arr_int[]=-2&arr_int[]=4&m_arr[test][0]=1&m_arr[test][1]=-2&m_arr[test][2]=4&m_m[0][mo1]=v&m_m[0][mo2]=v2&m_m[1][mo2]=v&m_m_m[mm][Name]=张三

Here is what you need and here are the few things you missed.

  • When working with https you need to set the TLSClientConfig
  • Missing X- prefix in custom headers
  • values.Encode() is the the wrong position.

Example: See https://www.cryptsy.com/pages/api

var (
    urlStr = "https://api.cryptsy.com/api"
    key    = "YOUR KEY"
    secret = "YOUR SECRECT"
    agent  = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36"
)

func main() {

    client := &http.Client{Transport: &http.Transport{
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }}

    values := url.Values{}
    //  $req['method'] = $method;
    values.Set("method", "getinfo")

    // $mt = explode(' ', microtime());
    // $req['nonce'] = $mt[1];
    values.Set("nonce", time.Nanosecond.String())

    //  $post_data = http_build_query($req, '', '&');
    encoded := values.Encode()

    mac := hmac.New(sha512.New, []byte(secret))
    mac.Write([]byte(encoded))

    //$sign = hash_hmac("sha512", $post_data, $secret);
    sign := fmt.Sprintf("%x", mac.Sum(nil))

    req, err := http.NewRequest("POST", urlStr, bytes.NewBufferString(encoded))

    if err != nil {
        log.Fatalln(err)
    }

    // generate the extra headers
    // $headers = array(
    // 'Sign: '.$sign,
    // 'Key: '.$key,
    // );
    req.Header.Set("X-Sign", sign)
    req.Header.Set("X-Key", key)

    //  curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; Cryptsy API PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
    req.Header.Set("User-Agent", agent)
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Add("Content-Length", strconv.Itoa(len(encoded)))

    //       $res = curl_exec($ch);
    resp, err := client.Do(req)

    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(resp.Status)

    data, _ := ioutil.ReadAll(resp.Body)
    fmt.Printf("%s", data)
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top