PHP与Golang的HTTP调用会得到不同的结果

huangapple go评论81阅读模式
英文:

PHP vs Golang http calls gets different results

问题

我正在尝试在Google App Engine Go中实现以下PHP代码:

<?php

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

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

    // 生成POST数据字符串
    $post_data = http_build_query($req, '', '&');
    $sign = '75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348';

    // 生成额外的头部信息
    $headers = array(
        'Sign: '.$sign,
        'Key: '.$key,
    );

    // 初始化curl句柄
    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);

    // 执行查询
    $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();

执行该代码后,会返回一个JSON数组。我尝试在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()
    // 读取响应
    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{})
    // 解析JSON响应
    err = json.Unmarshal(body, &result)
    if err != nil {
        c.Infof("Unmarshal: %v", err)
        c.Infof("%s", body)
        return nil, err
    }
    return result, nil
}

我收到一个错误消息,提示"无法授权请求-请检查您的POST数据"。有人能看出可能导致这个错误的原因吗?目前我最好的猜测是Go中的请求头是map[string][]string,而PHP中似乎是一个数组...

英文:

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

&lt;?php
function api_query(array $req = array()) {
$key = &#39;90294318da0162b082c3d27126be80c3873955f9&#39;;
$req[&#39;method&#39;] = &#39;getinfo&#39;;
$req[&#39;nonce&#39;] = 1394503747386411;
// generate the POST data string
$post_data = http_build_query($req, &#39;&#39;, &#39;&amp;&#39;);
$sign = &#39;75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348&#39;;
// generate the extra headers
$headers = array(
&#39;Sign: &#39;.$sign,
&#39;Key: &#39;.$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, &#39;Mozilla/4.0 (compatible; Cryptsy API PHP client; &#39;.php_uname(&#39;s&#39;).&#39;; PHP/&#39;.phpversion().&#39;)&#39;);
}
curl_setopt($ch, CURLOPT_URL, &#39;https://api.cryptsy.com/api&#39;);
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(&#39;Could not get reply: &#39;.curl_error($ch));
$dec = json_decode($res, true);
if (!$dec) throw new Exception(&#39;Invalid data received, please make sure connection is working and requested API exists&#39;);
echo &quot;&lt;pre&gt;&quot;.print_r($dec, true).&quot;&lt;/pre&gt;&quot;;
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 := &quot;https://api.cryptsy.com/api&quot;
APIKey := &quot;90294318da0162b082c3d27126be80c3873955f9&quot;
tr := urlfetch.Transport{Context: c}
values := url.Values{}
values.Set(&quot;method&quot;, &quot;getinfo&quot;)
values.Set(&quot;nonce&quot;, &quot;1394503747386411&quot;)
signature := &quot;75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348&quot;
req, err := http.NewRequest(&quot;POST&quot;, AuthAPI+&quot;?&quot;+values.Encode(), nil)
if err != nil {
c.Infof(&quot;API - Call - error 2 - %s&quot;, err.Error())
return nil, err
}
req.Header.Set(&quot;Key&quot;, APIKey)
req.Header.Set(&quot;Sign&quot;, signature)
c.Infof(&quot;req - %v&quot;, req)
resp, err := tr.RoundTrip(req)
if err != nil {
c.Errorf(&quot;API post error: %s&quot;, err)
return nil, err
}
defer resp.Body.Close()
//reading response
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
c.Errorf(&quot;API read error: could not read body: %s&quot;, err)
return nil, err
}
result := make(map[string]interface{})
//unmarshalling JSON response
err = json.Unmarshal(body, &amp;result)
if err != nil {
c.Infof(&quot;Unmarshal: %v&quot;, err)
c.Infof(&quot;%s&quot;, 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...

答案1

得分: 3

根据LeGEC的建议,你将POST数据放在URL的末尾,就像是一个GET请求。

尝试将以下代码替换为:

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)

这样做可以将数据作为JSON格式的请求体发送。

英文:

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(&quot;method&quot;, &quot;getinfo&quot;)
values.Set(&quot;nonce&quot;, &quot;1394503747386411&quot;)
signature := &quot;75da1e3ff750286bf73d03197f1b779fbfff963fd7402941ae326509a6615eacb839b44f236b4d5ee6cff39321e7b35e9563a9a2075e99df0f4ee3b732999348&quot;
req, err := http.NewRequest(&quot;POST&quot;, AuthAPI+&quot;?&quot;+values.Encode(), nil)

with

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

答案2

得分: 1

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)

// 输出结果
// 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]=张三

英文:

https://github.com/ctfang/http_build_query

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

答案3

得分: 0

这是你需要的内容,以及你错过的几个要点。

  • 在使用https时,需要设置TLSClientConfig
  • 自定义头部缺少X-前缀。
  • values.Encode()的位置错误。

示例:请参考 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)
}
英文:

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 = &quot;https://api.cryptsy.com/api&quot;
key    = &quot;YOUR KEY&quot;
secret = &quot;YOUR SECRECT&quot;
agent  = &quot;Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36&quot;
)
func main() {
client := &amp;http.Client{Transport: &amp;http.Transport{
TLSClientConfig: &amp;tls.Config{
InsecureSkipVerify: true,
},
}}
values := url.Values{}
//  $req[&#39;method&#39;] = $method;
values.Set(&quot;method&quot;, &quot;getinfo&quot;)
// $mt = explode(&#39; &#39;, microtime());
// $req[&#39;nonce&#39;] = $mt[1];
values.Set(&quot;nonce&quot;, time.Nanosecond.String())
//  $post_data = http_build_query($req, &#39;&#39;, &#39;&amp;&#39;);
encoded := values.Encode()
mac := hmac.New(sha512.New, []byte(secret))
mac.Write([]byte(encoded))
//$sign = hash_hmac(&quot;sha512&quot;, $post_data, $secret);
sign := fmt.Sprintf(&quot;%x&quot;, mac.Sum(nil))
req, err := http.NewRequest(&quot;POST&quot;, urlStr, bytes.NewBufferString(encoded))
if err != nil {
log.Fatalln(err)
}
// generate the extra headers
// $headers = array(
// &#39;Sign: &#39;.$sign,
// &#39;Key: &#39;.$key,
// );
req.Header.Set(&quot;X-Sign&quot;, sign)
req.Header.Set(&quot;X-Key&quot;, key)
//  curl_setopt($ch, CURLOPT_USERAGENT, &#39;Mozilla/4.0 (compatible; Cryptsy API PHP client; &#39;.php_uname(&#39;s&#39;).&#39;; PHP/&#39;.phpversion().&#39;)&#39;);
req.Header.Set(&quot;User-Agent&quot;, agent)
req.Header.Add(&quot;Content-Type&quot;, &quot;application/x-www-form-urlencoded&quot;)
req.Header.Add(&quot;Content-Length&quot;, 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(&quot;%s&quot;, data)
}

huangapple
  • 本文由 发表于 2014年3月11日 10:37:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/22315378.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定